cloudflare-next-intl 0.9.45 → 0.9.46
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.
|
@@ -22,8 +22,6 @@ function bindingsFromClause(clause) {
|
|
|
22
22
|
const namespaceMatch = /^\*\s*as\s+(\w+)$/.exec(clause.trim());
|
|
23
23
|
if (namespaceMatch)
|
|
24
24
|
return [namespaceMatch[1]];
|
|
25
|
-
if (clause.trim() === '*')
|
|
26
|
-
return [];
|
|
27
25
|
const braceMatch = /\{([^}]*)\}/.exec(clause);
|
|
28
26
|
if (braceMatch) {
|
|
29
27
|
for (const rawItem of braceMatch[1].split(',')) {
|
|
@@ -7,6 +7,10 @@ export declare function patchRouteMatching(code: string): string;
|
|
|
7
7
|
export declare function isOptimisticRoutingFile(id: string): boolean;
|
|
8
8
|
export declare function isOptimisticRoutingAlreadyFixed(code: string): boolean;
|
|
9
9
|
export declare function patchOptimisticRouting(code: string): string;
|
|
10
|
+
export declare function isPrefetchLearningFile(id: string): boolean;
|
|
11
|
+
export declare function isPrefetchLearningAlreadyFixed(code: string): boolean;
|
|
12
|
+
export declare function patchPrefetchLearning(code: string): string;
|
|
13
|
+
export declare function resolveVinextBrowserEntryPath(root?: string): string | null;
|
|
10
14
|
export declare function isAppPageRouteWiringFile(id: string): boolean;
|
|
11
15
|
export declare function resolveVinextAppPageRouteWiringPath(root?: string): string | null;
|
|
12
16
|
export declare function resolveVinextRouteMatchingPath(root?: string): string | null;
|
|
@@ -15,11 +19,14 @@ export interface SyncPatchVinextOnDiskOptions {
|
|
|
15
19
|
routeWiring?: boolean;
|
|
16
20
|
routeMatching?: boolean;
|
|
17
21
|
optimisticRouting?: boolean;
|
|
22
|
+
prefetchLearning?: boolean;
|
|
18
23
|
}
|
|
19
24
|
export declare function syncPatchVinextOnDisk(root?: string, options?: SyncPatchVinextOnDiskOptions): boolean;
|
|
25
|
+
export declare function bustVinextOptimizeDepsCache(cacheDir: string): boolean;
|
|
20
26
|
export interface VinextRouteWiringFixPluginOptions {
|
|
21
27
|
routeWiring?: boolean;
|
|
22
28
|
routeMatching?: boolean;
|
|
23
29
|
optimisticRouting?: boolean;
|
|
30
|
+
prefetchLearning?: boolean;
|
|
24
31
|
}
|
|
25
32
|
export declare function vinextRouteWiringFixPlugin(options?: VinextRouteWiringFixPluginOptions): Plugin;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, rmSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
const PREFETCH_LOADING_FN_RE = /function\s+getPrefetchLoadingEntry\s*\(\s*route\s*\)\s*\{[\s\S]*?return\s+getDefaultExport\s*\(\s*route\.loading\s*\)\s*\?[\s\S]*?:\s*null\s*;\s*\}/;
|
|
4
4
|
const FIXED_PREFETCH_LOADING_FN = `function getPrefetchLoadingEntry(route) {
|
|
@@ -113,14 +113,17 @@ export function isOptimisticRoutingAlreadyFixed(code) {
|
|
|
113
113
|
const hasRawPartsFix = code.includes("options.rawUrlParts[0] !== options.match.params.locale");
|
|
114
114
|
return hasLocalePrefixFirst && hasRawPartsFix;
|
|
115
115
|
}
|
|
116
|
-
const MATCH_OPTIMISTIC_ROUTE_RE = /function\s+matchOptimisticRouteManifestRoute\s*\(\s*options\s*\)\s*\{[\s\S]*?
|
|
117
|
-
const FIXED_MATCH_OPTIMISTIC_ROUTE = `function
|
|
116
|
+
const MATCH_OPTIMISTIC_ROUTE_RE = /function\s+matchOptimisticRouteManifestRoute\s*\(\s*options\s*\)\s*\{[\s\S]*?getRouteTrie\([\s\S]*?\)[\s\S]*?\n\}/;
|
|
117
|
+
const FIXED_MATCH_OPTIMISTIC_ROUTE = `function getActiveRouteLocale() {
|
|
118
|
+
return (typeof document !== "undefined" && (document.documentElement?.lang || document.cookie.match(/__user_locale_key__=([^;]+)/)?.[1])) || (typeof window !== "undefined" && window.__VINEXT_LOCALE__) || "en";
|
|
119
|
+
}
|
|
120
|
+
function matchOptimisticRouteManifestRoute(options) {
|
|
118
121
|
const urlParts = hrefToRouteParts(options.href, options.basePath);
|
|
119
122
|
if (urlParts === null) return null;
|
|
120
123
|
const trie = getRouteTrie(options.routeManifest);
|
|
121
124
|
const hasLeadingLocaleParam = Array.from(options.routeManifest?.segmentGraph?.routes?.values() ?? []).some((r) => r.patternParts?.[0] === ":locale");
|
|
122
125
|
if (hasLeadingLocaleParam) {
|
|
123
|
-
const activeLocale = (
|
|
126
|
+
const activeLocale = getActiveRouteLocale();
|
|
124
127
|
if (urlParts.normalized[0] !== activeLocale) {
|
|
125
128
|
const localeMatch = matchNode(trie, [activeLocale, ...urlParts.normalized], 0, []);
|
|
126
129
|
if (localeMatch !== null) {
|
|
@@ -147,14 +150,85 @@ export function patchOptimisticRouting(code) {
|
|
|
147
150
|
return code;
|
|
148
151
|
}
|
|
149
152
|
let result = code;
|
|
150
|
-
if (MATCH_OPTIMISTIC_ROUTE_RE.test(result)) {
|
|
153
|
+
if (!result.includes("hasLeadingLocaleParam") && MATCH_OPTIMISTIC_ROUTE_RE.test(result)) {
|
|
151
154
|
result = result.replace(MATCH_OPTIMISTIC_ROUTE_RE, FIXED_MATCH_OPTIMISTIC_ROUTE);
|
|
152
155
|
}
|
|
153
|
-
if (RESOLVE_OPTIMISTIC_NAV_PARAMS_RE.test(result)) {
|
|
156
|
+
if (!result.includes("options.rawUrlParts[0] !== options.match.params.locale") && RESOLVE_OPTIMISTIC_NAV_PARAMS_RE.test(result)) {
|
|
154
157
|
result = result.replace(RESOLVE_OPTIMISTIC_NAV_PARAMS_RE, FIXED_RESOLVE_OPTIMISTIC_NAV_PARAMS);
|
|
155
158
|
}
|
|
156
159
|
return result;
|
|
157
160
|
}
|
|
161
|
+
export function isPrefetchLearningFile(id) {
|
|
162
|
+
const cleanId = id.split("?")[0].replace(/\\/g, "/");
|
|
163
|
+
return cleanId.endsWith("/app-browser-entry.js") || cleanId.endsWith("/app-browser-entry.ts");
|
|
164
|
+
}
|
|
165
|
+
export function isPrefetchLearningAlreadyFixed(code) {
|
|
166
|
+
const hasFixedFunction = code.includes("isPendingNavigationTarget");
|
|
167
|
+
const hasFixedCallSite = code.includes("targetRscUrl: rscUrl,");
|
|
168
|
+
return hasFixedFunction && hasFixedCallSite;
|
|
169
|
+
}
|
|
170
|
+
const LEARN_TEMPLATES_FN_RE = /async\s+function\s+learnOptimisticRouteTemplatesFromPrefetchCache\s*\(\s*options\s*\)\s*\{[\s\S]*?await\s+Promise\.allSettled\(\s*learning\s*\);\s*\}/;
|
|
171
|
+
const FIXED_LEARN_TEMPLATES_FN = `async function learnOptimisticRouteTemplatesFromPrefetchCache(options) {
|
|
172
|
+
if (options.routeManifest === null) return;
|
|
173
|
+
const learning = [...optimisticRouteTemplateLearning.values()];
|
|
174
|
+
for (const [cacheKey, entry] of getPrefetchCache()) {
|
|
175
|
+
const sourceKey = getOptimisticPrefetchSourceKey({
|
|
176
|
+
cacheKey,
|
|
177
|
+
interceptionContext: options.interceptionContext,
|
|
178
|
+
mountedSlotsHeader: options.mountedSlotsHeader
|
|
179
|
+
});
|
|
180
|
+
if (optimisticRouteTemplateSources.has(sourceKey)) continue;
|
|
181
|
+
if (optimisticRouteTemplateLearning.has(sourceKey)) continue;
|
|
182
|
+
if (entry.prefetchKind === "route-tree") continue;
|
|
183
|
+
const isPendingNavigationTarget = !isSettledPrefetchCacheEntry(entry) && entry.pending !== void 0 && options.targetRscUrl !== void 0 && parsePrefetchCacheKey(cacheKey).rscUrl === options.targetRscUrl;
|
|
184
|
+
if (!isSettledPrefetchCacheEntry(entry) && !isPendingNavigationTarget) continue;
|
|
185
|
+
const promise = (async () => {
|
|
186
|
+
let settledEntry = entry;
|
|
187
|
+
if (!isSettledPrefetchCacheEntry(settledEntry)) {
|
|
188
|
+
await Promise.race([
|
|
189
|
+
settledEntry.pending?.catch(() => {}),
|
|
190
|
+
new Promise((resolve) => setTimeout(resolve, 3000))
|
|
191
|
+
]);
|
|
192
|
+
settledEntry = getPrefetchCache().get(cacheKey) ?? settledEntry;
|
|
193
|
+
if (!isSettledPrefetchCacheEntry(settledEntry)) return;
|
|
194
|
+
}
|
|
195
|
+
return learnOptimisticRouteTemplateFromPrefetch({
|
|
196
|
+
cacheKey,
|
|
197
|
+
entry: settledEntry,
|
|
198
|
+
interceptionContext: options.interceptionContext,
|
|
199
|
+
mountedSlotsHeader: options.mountedSlotsHeader,
|
|
200
|
+
routeManifest: options.routeManifest
|
|
201
|
+
});
|
|
202
|
+
})().then((learned) => {
|
|
203
|
+
if (learned) optimisticRouteTemplateSources.add(sourceKey);
|
|
204
|
+
}).finally(() => {
|
|
205
|
+
optimisticRouteTemplateLearning.delete(sourceKey);
|
|
206
|
+
});
|
|
207
|
+
optimisticRouteTemplateLearning.set(sourceKey, promise);
|
|
208
|
+
learning.push(promise);
|
|
209
|
+
}
|
|
210
|
+
if (learning.length === 0) return;
|
|
211
|
+
await Promise.allSettled(learning);
|
|
212
|
+
}`;
|
|
213
|
+
const LEARN_TEMPLATES_CALL_RE = /(await\s+learnOptimisticRouteTemplatesFromPrefetchCache\(\s*\{\s*)interceptionContext:\s*requestInterceptionContext,/;
|
|
214
|
+
const FIXED_LEARN_TEMPLATES_CALL = "$1interceptionContext: requestInterceptionContext,\n\t\t\t\t\t\t\ttargetRscUrl: rscUrl,";
|
|
215
|
+
export function patchPrefetchLearning(code) {
|
|
216
|
+
if (isPrefetchLearningAlreadyFixed(code)) {
|
|
217
|
+
return code;
|
|
218
|
+
}
|
|
219
|
+
let result = code;
|
|
220
|
+
if (!result.includes("isPendingNavigationTarget") && LEARN_TEMPLATES_FN_RE.test(result)) {
|
|
221
|
+
result = result.replace(LEARN_TEMPLATES_FN_RE, FIXED_LEARN_TEMPLATES_FN);
|
|
222
|
+
}
|
|
223
|
+
if (!result.includes("targetRscUrl: rscUrl,") && LEARN_TEMPLATES_CALL_RE.test(result)) {
|
|
224
|
+
result = result.replace(LEARN_TEMPLATES_CALL_RE, FIXED_LEARN_TEMPLATES_CALL);
|
|
225
|
+
}
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
export function resolveVinextBrowserEntryPath(root = process.cwd()) {
|
|
229
|
+
const directPath = resolve(root, "node_modules/vinext/dist/server/app-browser-entry.js");
|
|
230
|
+
return existsSync(directPath) ? directPath : null;
|
|
231
|
+
}
|
|
158
232
|
export function isAppPageRouteWiringFile(id) {
|
|
159
233
|
const cleanId = id.split("?")[0].replace(/\\/g, "/");
|
|
160
234
|
return cleanId.endsWith("/app-page-route-wiring.js") || cleanId.endsWith("/app-page-route-wiring.tsx") || cleanId.endsWith("/app-page-route-wiring.ts");
|
|
@@ -172,7 +246,7 @@ export function resolveVinextOptimisticRoutingPath(root = process.cwd()) {
|
|
|
172
246
|
return existsSync(directPath) ? directPath : null;
|
|
173
247
|
}
|
|
174
248
|
export function syncPatchVinextOnDisk(root = process.cwd(), options = {}) {
|
|
175
|
-
const { routeWiring = true, routeMatching = true, optimisticRouting = true } = options;
|
|
249
|
+
const { routeWiring = true, routeMatching = true, optimisticRouting = true, prefetchLearning = true } = options;
|
|
176
250
|
let changed = false;
|
|
177
251
|
const wiringPath = routeWiring ? resolveVinextAppPageRouteWiringPath(root) : null;
|
|
178
252
|
if (wiringPath) {
|
|
@@ -184,6 +258,9 @@ export function syncPatchVinextOnDisk(root = process.cwd(), options = {}) {
|
|
|
184
258
|
writeFileSync(wiringPath, patched, "utf8");
|
|
185
259
|
changed = true;
|
|
186
260
|
}
|
|
261
|
+
else {
|
|
262
|
+
console.warn(`[cfni:vinext-route-wiring-fix] ${wiringPath} does not match the expected shape for patchAppPageRouteWiring — this vinext version may have changed; the route-wiring fix was NOT applied.`);
|
|
263
|
+
}
|
|
187
264
|
}
|
|
188
265
|
}
|
|
189
266
|
catch {
|
|
@@ -199,6 +276,9 @@ export function syncPatchVinextOnDisk(root = process.cwd(), options = {}) {
|
|
|
199
276
|
writeFileSync(matchingPath, patched, "utf8");
|
|
200
277
|
changed = true;
|
|
201
278
|
}
|
|
279
|
+
else {
|
|
280
|
+
console.warn(`[cfni:vinext-route-wiring-fix] ${matchingPath} does not match the expected shape for patchRouteMatching — this vinext version may have changed; the route-matching fix was NOT applied.`);
|
|
281
|
+
}
|
|
202
282
|
}
|
|
203
283
|
}
|
|
204
284
|
catch {
|
|
@@ -214,6 +294,27 @@ export function syncPatchVinextOnDisk(root = process.cwd(), options = {}) {
|
|
|
214
294
|
writeFileSync(optimisticPath, patched, "utf8");
|
|
215
295
|
changed = true;
|
|
216
296
|
}
|
|
297
|
+
else {
|
|
298
|
+
console.warn(`[cfni:vinext-route-wiring-fix] ${optimisticPath} does not match the expected shape for patchOptimisticRouting — this vinext version may have changed; the optimistic-routing fix was NOT applied.`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const browserEntryPath = prefetchLearning ? resolveVinextBrowserEntryPath(root) : null;
|
|
306
|
+
if (browserEntryPath) {
|
|
307
|
+
try {
|
|
308
|
+
const content = readFileSync(browserEntryPath, "utf8");
|
|
309
|
+
if (!isPrefetchLearningAlreadyFixed(content)) {
|
|
310
|
+
const patched = patchPrefetchLearning(content);
|
|
311
|
+
if (patched !== content) {
|
|
312
|
+
writeFileSync(browserEntryPath, patched, "utf8");
|
|
313
|
+
changed = true;
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
console.warn(`[cfni:vinext-route-wiring-fix] ${browserEntryPath} does not match the expected shape for patchPrefetchLearning — this vinext version may have changed; the prefetch-learning fix was NOT applied.`);
|
|
317
|
+
}
|
|
217
318
|
}
|
|
218
319
|
}
|
|
219
320
|
catch {
|
|
@@ -221,16 +322,39 @@ export function syncPatchVinextOnDisk(root = process.cwd(), options = {}) {
|
|
|
221
322
|
}
|
|
222
323
|
return changed;
|
|
223
324
|
}
|
|
325
|
+
export function bustVinextOptimizeDepsCache(cacheDir) {
|
|
326
|
+
let removed = false;
|
|
327
|
+
for (const sub of ["deps", "deps_ssr", "deps_rsc"]) {
|
|
328
|
+
const dir = resolve(cacheDir, sub);
|
|
329
|
+
if (!existsSync(dir))
|
|
330
|
+
continue;
|
|
331
|
+
try {
|
|
332
|
+
rmSync(dir, { recursive: true, force: true });
|
|
333
|
+
removed = true;
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return removed;
|
|
339
|
+
}
|
|
224
340
|
export function vinextRouteWiringFixPlugin(options = {}) {
|
|
225
341
|
const routeWiring = options.routeWiring !== false;
|
|
226
342
|
const routeMatching = options.routeMatching !== false;
|
|
227
343
|
const optimisticRouting = options.optimisticRouting !== false;
|
|
344
|
+
const prefetchLearning = options.prefetchLearning !== false;
|
|
228
345
|
return {
|
|
229
346
|
name: "cfni:vinext-route-wiring-fix",
|
|
230
347
|
enforce: "pre",
|
|
231
348
|
configResolved(config) {
|
|
232
349
|
const root = config.root || process.cwd();
|
|
233
|
-
syncPatchVinextOnDisk(root, { routeWiring, routeMatching, optimisticRouting });
|
|
350
|
+
const changed = syncPatchVinextOnDisk(root, { routeWiring, routeMatching, optimisticRouting, prefetchLearning });
|
|
351
|
+
if (changed) {
|
|
352
|
+
const cacheDir = config.cacheDir || resolve(root, "node_modules/.vite");
|
|
353
|
+
const busted = bustVinextOptimizeDepsCache(cacheDir);
|
|
354
|
+
if (busted) {
|
|
355
|
+
console.log("[cfni:vinext-route-wiring-fix] patched vinext on disk and cleared its stale Vite optimizeDeps cache — dependencies will re-bundle on next request.");
|
|
356
|
+
}
|
|
357
|
+
}
|
|
234
358
|
},
|
|
235
359
|
transform(code, id) {
|
|
236
360
|
if (routeWiring && isAppPageRouteWiringFile(id)) {
|
|
@@ -253,6 +377,16 @@ export function vinextRouteWiringFixPlugin(options = {}) {
|
|
|
253
377
|
map: null,
|
|
254
378
|
};
|
|
255
379
|
}
|
|
380
|
+
if (prefetchLearning && isPrefetchLearningFile(id)) {
|
|
381
|
+
if (isPrefetchLearningAlreadyFixed(code)) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const patched = patchPrefetchLearning(code);
|
|
385
|
+
return {
|
|
386
|
+
code: patched,
|
|
387
|
+
map: null,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
256
390
|
if (optimisticRouting && isOptimisticRoutingFile(id)) {
|
|
257
391
|
if (isOptimisticRoutingAlreadyFixed(code)) {
|
|
258
392
|
return;
|