mnfst 0.5.42 → 0.5.44
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/manifest.router.js +87 -4
- package/package.json +1 -1
package/dist/manifest.router.js
CHANGED
|
@@ -228,6 +228,82 @@ function shouldSkipStickyLocaleForLogicalSegments(segments) {
|
|
|
228
228
|
return STICKY_LOCALE_STATIC_FILE_EXT.has(ext);
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
+
// manifest.prerender.localeRouteExclude → JSON array in meta; logical path prefixes (after locale).
|
|
232
|
+
function parseLocaleRouteExcludePatterns() {
|
|
233
|
+
const meta = document.querySelector('meta[name="manifest:locale-route-exclude"]');
|
|
234
|
+
const raw = meta?.getAttribute('content') || '';
|
|
235
|
+
if (raw.trim().startsWith('[')) {
|
|
236
|
+
try {
|
|
237
|
+
const parsed = JSON.parse(raw.replace(/"/g, '"'));
|
|
238
|
+
if (Array.isArray(parsed)) {
|
|
239
|
+
return parsed.map((s) => String(s).trim()).filter(Boolean);
|
|
240
|
+
}
|
|
241
|
+
} catch {
|
|
242
|
+
/* fall through */
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const legacy = document.querySelector('meta[name="manifest:locale-sticky-exclude"]');
|
|
246
|
+
const legacyRaw = legacy?.getAttribute('content') || '';
|
|
247
|
+
if (!legacyRaw.trim()) return [];
|
|
248
|
+
return legacyRaw.split(',').map((s) => s.trim().replace(/^\/+/, '').split('/')[0]).filter(Boolean);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function logicalPathMatchesLocaleRouteExclude(segments, patterns) {
|
|
252
|
+
if (!patterns.length || !segments.length) return false;
|
|
253
|
+
const lower = segments.map((s) => s.toLowerCase());
|
|
254
|
+
for (const pattern of patterns) {
|
|
255
|
+
const p = String(pattern)
|
|
256
|
+
.trim()
|
|
257
|
+
.replace(/^\/+/, '')
|
|
258
|
+
.split('/')
|
|
259
|
+
.filter(Boolean)
|
|
260
|
+
.map((x) => x.toLowerCase());
|
|
261
|
+
if (p.length === 0) continue;
|
|
262
|
+
if (lower.length < p.length) continue;
|
|
263
|
+
let match = true;
|
|
264
|
+
for (let i = 0; i < p.length; i++) {
|
|
265
|
+
if (lower[i] !== p[i]) {
|
|
266
|
+
match = false;
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (match) return true;
|
|
271
|
+
}
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// /fr/legal/terms → /legal/terms when patterns include "legal" or "legal/terms" (prefix match).
|
|
276
|
+
function normalizeRedundantLocalePrefixInUrl() {
|
|
277
|
+
if (!isPrerenderedStaticBuild()) return false;
|
|
278
|
+
const codes = getLocalizationCodesFromManifest();
|
|
279
|
+
const patterns = parseLocaleRouteExcludePatterns();
|
|
280
|
+
if (!codes.length || !patterns.length) return false;
|
|
281
|
+
|
|
282
|
+
const segs = logicalSegmentsFromPathname(window.location.pathname);
|
|
283
|
+
if (segs.length < 2) return false;
|
|
284
|
+
if (!codes.includes(segs[0])) return false;
|
|
285
|
+
const rest = segs.slice(1);
|
|
286
|
+
if (!logicalPathMatchesLocaleRouteExclude(rest, patterns)) return false;
|
|
287
|
+
|
|
288
|
+
const newLogical = '/' + rest.join('/');
|
|
289
|
+
const base = getBasePath();
|
|
290
|
+
let newPathname = base ? base.replace(/\/+$/, '') + newLogical : newLogical;
|
|
291
|
+
newPathname = newPathname.replace(/\/{2,}/g, '/');
|
|
292
|
+
if (!newPathname.startsWith('/')) newPathname = '/' + newPathname;
|
|
293
|
+
if (newPathname === window.location.pathname) return false;
|
|
294
|
+
|
|
295
|
+
const prevLogical = pathnameToLogical(window.location.pathname);
|
|
296
|
+
const u = new URL(window.location.href);
|
|
297
|
+
u.pathname = newPathname;
|
|
298
|
+
history.replaceState(null, '', u.toString());
|
|
299
|
+
currentRoute = pathnameToLogical(newPathname);
|
|
300
|
+
const np = currentRoute === '/' ? '/' : String(currentRoute).replace(/^\/|\/$/g, '');
|
|
301
|
+
window.dispatchEvent(new CustomEvent('manifest:route-change', {
|
|
302
|
+
detail: { from: prevLogical, to: currentRoute, normalizedPath: np }
|
|
303
|
+
}));
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
|
|
231
307
|
// When the URL already has a locale prefix (e.g. /zh/pricing), keep it for same-origin links
|
|
232
308
|
// that omit the prefix (/articles → /zh/articles). No-op on default-locale URLs (/pricing).
|
|
233
309
|
function applyStickyLocaleToPathname(absolutePathname) {
|
|
@@ -242,6 +318,12 @@ function applyStickyLocaleToPathname(absolutePathname) {
|
|
|
242
318
|
if (targetSegs.length && codes.includes(targetSegs[0])) {
|
|
243
319
|
return absolutePathname;
|
|
244
320
|
}
|
|
321
|
+
|
|
322
|
+
const routeEx = parseLocaleRouteExcludePatterns();
|
|
323
|
+
if (routeEx.length && logicalPathMatchesLocaleRouteExclude(targetSegs, routeEx)) {
|
|
324
|
+
return absolutePathname;
|
|
325
|
+
}
|
|
326
|
+
|
|
245
327
|
if (shouldSkipStickyLocaleForLogicalSegments(targetSegs)) {
|
|
246
328
|
return absolutePathname;
|
|
247
329
|
}
|
|
@@ -277,6 +359,7 @@ async function handleRouteChange() {
|
|
|
277
359
|
const newRoute = pathnameToLogical(pathname);
|
|
278
360
|
if (newRoute === currentRoute) return;
|
|
279
361
|
|
|
362
|
+
const prevRoute = currentRoute;
|
|
280
363
|
currentRoute = newRoute;
|
|
281
364
|
|
|
282
365
|
// Handle scrolling based on whether this is an anchor link or route change
|
|
@@ -317,7 +400,7 @@ async function handleRouteChange() {
|
|
|
317
400
|
// Emit route change event
|
|
318
401
|
window.dispatchEvent(new CustomEvent('manifest:route-change', {
|
|
319
402
|
detail: {
|
|
320
|
-
from:
|
|
403
|
+
from: prevRoute,
|
|
321
404
|
to: newRoute,
|
|
322
405
|
normalizedPath: newRoute === '/' ? '/' : newRoute.replace(/^\/|\/$/g, '')
|
|
323
406
|
}
|
|
@@ -389,9 +472,9 @@ function installMpaStickyLocaleLinks() {
|
|
|
389
472
|
|
|
390
473
|
const path = url.pathname.replace(/\/$/, '') || '/';
|
|
391
474
|
const adjusted = applyStickyLocaleToPathname(path);
|
|
392
|
-
if (adjusted === path) return;
|
|
393
|
-
|
|
394
475
|
event.preventDefault();
|
|
476
|
+
event.stopPropagation();
|
|
477
|
+
event.stopImmediatePropagation();
|
|
395
478
|
url.pathname = adjusted;
|
|
396
479
|
|
|
397
480
|
const dest = url.toString();
|
|
@@ -489,8 +572,8 @@ function interceptLinkClicks() {
|
|
|
489
572
|
|
|
490
573
|
// Initialize navigation
|
|
491
574
|
function initializeNavigation() {
|
|
492
|
-
// Set initial route (logical path for matching)
|
|
493
575
|
currentRoute = pathnameToLogical(window.location.pathname);
|
|
576
|
+
normalizeRedundantLocalePrefixInUrl();
|
|
494
577
|
|
|
495
578
|
// In prerendered/static output, use default browser navigation (no SPA interception)
|
|
496
579
|
if (!isPrerenderedStaticBuild()) {
|