vinext 0.1.0 → 0.1.2
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/README.md +2 -5
- package/dist/build/assets-ignore.d.ts +32 -0
- package/dist/build/assets-ignore.js +48 -0
- package/dist/build/client-build-config.d.ts +33 -1
- package/dist/build/client-build-config.js +66 -1
- package/dist/check.js +4 -3
- package/dist/cli.js +2 -0
- package/dist/client/navigation-runtime.d.ts +11 -2
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/client/vinext-next-data.d.ts +2 -1
- package/dist/client/window-next.d.ts +6 -4
- package/dist/config/config-matchers.d.ts +31 -5
- package/dist/config/config-matchers.js +50 -3
- package/dist/config/next-config.d.ts +29 -3
- package/dist/config/next-config.js +32 -2
- package/dist/deploy.js +47 -304
- package/dist/entries/app-rsc-entry.d.ts +8 -2
- package/dist/entries/app-rsc-entry.js +61 -5
- package/dist/entries/app-rsc-manifest.js +20 -2
- package/dist/entries/pages-client-entry.js +1 -1
- package/dist/entries/pages-server-entry.js +16 -7
- package/dist/index.d.ts +0 -2
- package/dist/index.js +233 -280
- package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
- package/dist/plugins/dynamic-preload-metadata.js +415 -0
- package/dist/plugins/og-assets.js +2 -2
- package/dist/plugins/optimize-imports.d.ts +8 -4
- package/dist/plugins/optimize-imports.js +16 -12
- package/dist/plugins/postcss.js +18 -14
- package/dist/plugins/require-context.d.ts +6 -0
- package/dist/plugins/require-context.js +184 -0
- package/dist/plugins/sass.d.ts +53 -24
- package/dist/plugins/sass.js +249 -1
- package/dist/plugins/wasm-module-import.d.ts +15 -0
- package/dist/plugins/wasm-module-import.js +50 -0
- package/dist/routing/app-route-graph.d.ts +35 -2
- package/dist/routing/app-route-graph.js +179 -8
- package/dist/routing/file-matcher.js +1 -1
- package/dist/routing/route-pattern.d.ts +2 -1
- package/dist/routing/route-pattern.js +16 -1
- package/dist/server/api-handler.js +4 -0
- package/dist/server/app-browser-entry.js +155 -215
- package/dist/server/app-browser-error.d.ts +4 -1
- package/dist/server/app-browser-error.js +7 -1
- package/dist/server/app-browser-history-controller.d.ts +104 -0
- package/dist/server/app-browser-history-controller.js +210 -0
- package/dist/server/app-browser-interception-context.d.ts +2 -1
- package/dist/server/app-browser-interception-context.js +15 -2
- package/dist/server/app-browser-navigation-controller.d.ts +13 -2
- package/dist/server/app-browser-navigation-controller.js +83 -4
- package/dist/server/app-browser-popstate.d.ts +12 -3
- package/dist/server/app-browser-popstate.js +19 -4
- package/dist/server/app-browser-rsc-redirect.d.ts +11 -2
- package/dist/server/app-browser-rsc-redirect.js +30 -8
- package/dist/server/app-browser-state.d.ts +3 -0
- package/dist/server/app-browser-state.js +10 -10
- package/dist/server/app-browser-visible-commit.js +10 -8
- package/dist/server/app-fallback-renderer.d.ts +2 -1
- package/dist/server/app-fallback-renderer.js +3 -1
- package/dist/server/app-history-state.d.ts +45 -1
- package/dist/server/app-history-state.js +109 -1
- package/dist/server/app-middleware.js +1 -0
- package/dist/server/app-optimistic-routing.js +22 -1
- package/dist/server/app-page-boundary-render.d.ts +2 -1
- package/dist/server/app-page-boundary-render.js +45 -21
- package/dist/server/app-page-cache.js +9 -7
- package/dist/server/app-page-dispatch.d.ts +14 -0
- package/dist/server/app-page-dispatch.js +21 -6
- package/dist/server/app-page-element-builder.d.ts +23 -2
- package/dist/server/app-page-element-builder.js +58 -17
- package/dist/server/app-page-execution.d.ts +1 -1
- package/dist/server/app-page-execution.js +32 -17
- package/dist/server/app-page-render.d.ts +7 -1
- package/dist/server/app-page-render.js +11 -16
- package/dist/server/app-page-request.d.ts +9 -6
- package/dist/server/app-page-request.js +14 -10
- package/dist/server/app-page-response.d.ts +2 -2
- package/dist/server/app-page-response.js +2 -2
- package/dist/server/app-page-route-wiring.d.ts +3 -1
- package/dist/server/app-page-route-wiring.js +10 -8
- package/dist/server/app-page-stream.d.ts +37 -7
- package/dist/server/app-page-stream.js +36 -6
- package/dist/server/app-pages-bridge.d.ts +16 -0
- package/dist/server/app-pages-bridge.js +23 -3
- package/dist/server/app-route-handler-cache.d.ts +1 -0
- package/dist/server/app-route-handler-cache.js +1 -0
- package/dist/server/app-route-handler-dispatch.d.ts +1 -0
- package/dist/server/app-route-handler-dispatch.js +2 -0
- package/dist/server/app-route-handler-execution.d.ts +1 -0
- package/dist/server/app-route-handler-execution.js +1 -0
- package/dist/server/app-route-handler-response.js +11 -10
- package/dist/server/app-route-handler-runtime.d.ts +1 -0
- package/dist/server/app-route-handler-runtime.js +15 -3
- package/dist/server/app-rsc-handler.d.ts +1 -0
- package/dist/server/app-rsc-handler.js +5 -4
- package/dist/server/app-rsc-response-finalizer.js +1 -1
- package/dist/server/app-rsc-route-matching.d.ts +20 -1
- package/dist/server/app-rsc-route-matching.js +29 -4
- package/dist/server/app-server-action-execution.d.ts +22 -1
- package/dist/server/app-server-action-execution.js +73 -12
- package/dist/server/app-ssr-entry.d.ts +6 -0
- package/dist/server/app-ssr-entry.js +19 -3
- package/dist/server/app-ssr-stream.js +9 -1
- package/dist/server/dev-lockfile.js +2 -1
- package/dist/server/dev-server.d.ts +1 -1
- package/dist/server/dev-server.js +97 -43
- package/dist/server/headers.d.ts +8 -1
- package/dist/server/headers.js +8 -1
- package/dist/server/instrumentation-runtime.d.ts +6 -0
- package/dist/server/instrumentation-runtime.js +8 -0
- package/dist/server/isr-cache.d.ts +37 -1
- package/dist/server/isr-cache.js +85 -1
- package/dist/server/isr-decision.d.ts +79 -0
- package/dist/server/isr-decision.js +70 -0
- package/dist/server/metadata-route-response.js +5 -3
- package/dist/server/middleware-runtime.d.ts +13 -0
- package/dist/server/middleware-runtime.js +11 -7
- package/dist/server/middleware.js +1 -0
- package/dist/server/navigation-planner.d.ts +62 -1
- package/dist/server/navigation-planner.js +193 -3
- package/dist/server/navigation-trace.d.ts +12 -2
- package/dist/server/navigation-trace.js +11 -1
- package/dist/server/normalize-path.d.ts +0 -8
- package/dist/server/normalize-path.js +3 -1
- package/dist/server/otel-tracer-extension.d.ts +45 -0
- package/dist/server/otel-tracer-extension.js +89 -0
- package/dist/server/pages-api-route.d.ts +14 -3
- package/dist/server/pages-api-route.js +6 -1
- package/dist/server/pages-asset-tags.d.ts +15 -4
- package/dist/server/pages-asset-tags.js +18 -12
- package/dist/server/pages-data-route.js +5 -1
- package/dist/server/pages-node-compat.d.ts +5 -11
- package/dist/server/pages-node-compat.js +175 -118
- package/dist/server/pages-page-data.d.ts +38 -7
- package/dist/server/pages-page-data.js +64 -18
- package/dist/server/pages-page-handler.d.ts +10 -2
- package/dist/server/pages-page-handler.js +49 -20
- package/dist/server/pages-page-response.d.ts +55 -2
- package/dist/server/pages-page-response.js +74 -6
- package/dist/server/pages-readiness.d.ts +36 -0
- package/dist/server/pages-readiness.js +21 -0
- package/dist/server/pages-request-pipeline.d.ts +113 -0
- package/dist/server/pages-request-pipeline.js +230 -0
- package/dist/server/pages-revalidate.d.ts +15 -0
- package/dist/server/pages-revalidate.js +19 -0
- package/dist/server/prod-server.d.ts +45 -3
- package/dist/server/prod-server.js +182 -234
- package/dist/server/socket-error-backstop.d.ts +19 -1
- package/dist/server/socket-error-backstop.js +77 -4
- package/dist/shims/app-router-scroll.js +22 -4
- package/dist/shims/cache-runtime.js +39 -2
- package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
- package/dist/shims/dynamic-preload-chunks.js +77 -0
- package/dist/shims/dynamic.d.ts +4 -0
- package/dist/shims/dynamic.js +4 -2
- package/dist/shims/error-boundary.d.ts +17 -7
- package/dist/shims/error-boundary.js +8 -1
- package/dist/shims/error.js +37 -11
- package/dist/shims/fetch-cache.d.ts +22 -1
- package/dist/shims/fetch-cache.js +28 -1
- package/dist/shims/hash-scroll.d.ts +1 -0
- package/dist/shims/hash-scroll.js +3 -1
- package/dist/shims/head.js +6 -1
- package/dist/shims/headers.d.ts +16 -2
- package/dist/shims/headers.js +37 -1
- package/dist/shims/image-config.js +7 -1
- package/dist/shims/internal/app-route-detection.d.ts +6 -3
- package/dist/shims/internal/app-route-detection.js +10 -6
- package/dist/shims/internal/app-router-context.d.ts +5 -0
- package/dist/shims/internal/link-status-registry.d.ts +43 -0
- package/dist/shims/internal/link-status-registry.js +42 -0
- package/dist/shims/internal/route-pattern-for-warning.d.ts +27 -0
- package/dist/shims/internal/route-pattern-for-warning.js +40 -0
- package/dist/shims/internal/utils.d.ts +1 -0
- package/dist/shims/link.js +20 -6
- package/dist/shims/metadata.d.ts +6 -2
- package/dist/shims/metadata.js +32 -14
- package/dist/shims/navigation.d.ts +9 -18
- package/dist/shims/navigation.js +96 -23
- package/dist/shims/router-state.d.ts +1 -0
- package/dist/shims/router-state.js +2 -0
- package/dist/shims/router.d.ts +6 -3
- package/dist/shims/router.js +156 -22
- package/dist/shims/script-nonce-context.d.ts +1 -1
- package/dist/shims/script-nonce-context.js +11 -3
- package/dist/shims/server.d.ts +17 -1
- package/dist/shims/server.js +31 -6
- package/dist/shims/slot.js +1 -1
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/typegen.js +1 -0
- package/dist/utils/client-build-manifest.d.ts +8 -1
- package/dist/utils/client-build-manifest.js +41 -6
- package/dist/utils/client-entry-manifest.d.ts +11 -0
- package/dist/utils/client-entry-manifest.js +29 -0
- package/dist/utils/client-runtime-metadata.d.ts +45 -0
- package/dist/utils/client-runtime-metadata.js +63 -0
- package/dist/utils/hash.d.ts +17 -1
- package/dist/utils/hash.js +36 -1
- package/dist/utils/lazy-chunks.d.ts +27 -1
- package/dist/utils/lazy-chunks.js +65 -1
- package/dist/utils/manifest-paths.d.ts +20 -2
- package/dist/utils/manifest-paths.js +38 -3
- package/dist/utils/path.d.ts +2 -1
- package/dist/utils/path.js +5 -1
- package/package.json +6 -2
package/dist/shims/head.js
CHANGED
|
@@ -104,6 +104,10 @@ const SELF_CLOSING_HEAD_TAGS = new Set([
|
|
|
104
104
|
]);
|
|
105
105
|
/** Tags whose content is raw text — closing-tag sequences must be escaped during SSR. */
|
|
106
106
|
const RAW_CONTENT_TAGS = new Set(["script", "style"]);
|
|
107
|
+
const INLINE_CLOSE_TAG_RES = {
|
|
108
|
+
script: /<\/(script)/gi,
|
|
109
|
+
style: /<\/(style)/gi
|
|
110
|
+
};
|
|
107
111
|
function warnDisallowedHeadTag(tag) {
|
|
108
112
|
if (process.env.NODE_ENV !== "production") console.warn(`[vinext] <Head> ignoring disallowed tag <${tag}>. Only ${ALLOWED_HEAD_TAGS_LIST} are allowed.`);
|
|
109
113
|
}
|
|
@@ -247,7 +251,8 @@ function escapeAttr(s) {
|
|
|
247
251
|
* context but prevents the HTML parser from seeing a closing tag.
|
|
248
252
|
*/
|
|
249
253
|
function escapeInlineContent(content, tag) {
|
|
250
|
-
const pattern =
|
|
254
|
+
const pattern = INLINE_CLOSE_TAG_RES[tag];
|
|
255
|
+
if (!pattern) return content;
|
|
251
256
|
return content.replace(pattern, "<\\/$1");
|
|
252
257
|
}
|
|
253
258
|
function getDangerouslySetInnerHTML(value) {
|
package/dist/shims/headers.d.ts
CHANGED
|
@@ -18,12 +18,24 @@ type HeadersAccessPhase = "render" | "action" | "route-handler";
|
|
|
18
18
|
type VinextHeadersShimState = {
|
|
19
19
|
headersContext: HeadersContext | null;
|
|
20
20
|
dynamicUsageDetected: boolean;
|
|
21
|
-
renderRequestApiUsage: Set<RenderRequestApiKind>;
|
|
21
|
+
renderRequestApiUsage: Set<RenderRequestApiKind>;
|
|
22
|
+
connectionProbe: ConnectionProbeState | null; /** Error recorded by throwIfInsideCacheScope for dev diagnostics, persists even if caught by user code. */
|
|
22
23
|
invalidDynamicUsageError: unknown;
|
|
23
24
|
pendingSetCookies: string[];
|
|
24
25
|
draftModeCookieHeader: string | null;
|
|
25
26
|
phase: HeadersAccessPhase;
|
|
26
27
|
};
|
|
28
|
+
type ConnectionProbeState = {
|
|
29
|
+
interrupted: boolean;
|
|
30
|
+
interrupt: () => void;
|
|
31
|
+
pending: Promise<never>;
|
|
32
|
+
};
|
|
33
|
+
type ConnectionProbeResult<T> = {
|
|
34
|
+
completed: true;
|
|
35
|
+
result: T;
|
|
36
|
+
} | {
|
|
37
|
+
completed: false;
|
|
38
|
+
};
|
|
27
39
|
/**
|
|
28
40
|
* Dynamic usage flag — set when a component calls connection(), cookies(),
|
|
29
41
|
* headers(), or noStore() during rendering. When true, ISR caching is
|
|
@@ -35,6 +47,8 @@ type VinextHeadersShimState = {
|
|
|
35
47
|
*/
|
|
36
48
|
declare function markDynamicUsage(): void;
|
|
37
49
|
declare function markRenderRequestApiUsage(kind: RenderRequestApiKind): void;
|
|
50
|
+
declare function runWithConnectionProbe<T>(fn: () => T | Promise<T>): Promise<ConnectionProbeResult<T>>;
|
|
51
|
+
declare function suspendConnectionProbe(): Promise<never> | null;
|
|
38
52
|
declare function peekRenderRequestApiUsage(): RenderRequestApiKind[];
|
|
39
53
|
declare function consumeRenderRequestApiUsage(): RenderRequestApiKind[];
|
|
40
54
|
/**
|
|
@@ -236,4 +250,4 @@ declare class RequestCookies {
|
|
|
236
250
|
toString(): string;
|
|
237
251
|
}
|
|
238
252
|
//#endregion
|
|
239
|
-
export { HeadersAccessPhase, HeadersContext, type RequestCookies, VinextHeadersShimState, applyMiddlewareRequestHeaders, consumeDynamicUsage, consumeInvalidDynamicUsageError, consumeRenderRequestApiUsage, cookies, draftMode, getAndClearPendingCookies, getDraftModeCookieHeader, getHeadersAccessPhase, getHeadersContext, headers, headersContextFromRequest, isDraftModeRequest, markDynamicUsage, markRenderRequestApiUsage, peekDynamicUsage, peekRenderRequestApiUsage, runWithHeadersContext, setHeadersAccessPhase, setHeadersContext, throwIfInsideCacheScope };
|
|
253
|
+
export { HeadersAccessPhase, HeadersContext, type RequestCookies, VinextHeadersShimState, applyMiddlewareRequestHeaders, consumeDynamicUsage, consumeInvalidDynamicUsageError, consumeRenderRequestApiUsage, cookies, draftMode, getAndClearPendingCookies, getDraftModeCookieHeader, getHeadersAccessPhase, getHeadersContext, headers, headersContextFromRequest, isDraftModeRequest, markDynamicUsage, markRenderRequestApiUsage, peekDynamicUsage, peekRenderRequestApiUsage, runWithConnectionProbe, runWithHeadersContext, setHeadersAccessPhase, setHeadersContext, suspendConnectionProbe, throwIfInsideCacheScope };
|
package/dist/shims/headers.js
CHANGED
|
@@ -21,6 +21,7 @@ const _fallbackState = _g[_FALLBACK_KEY] ??= {
|
|
|
21
21
|
headersContext: null,
|
|
22
22
|
dynamicUsageDetected: false,
|
|
23
23
|
renderRequestApiUsage: /* @__PURE__ */ new Set(),
|
|
24
|
+
connectionProbe: null,
|
|
24
25
|
invalidDynamicUsageError: null,
|
|
25
26
|
pendingSetCookies: [],
|
|
26
27
|
draftModeCookieHeader: null,
|
|
@@ -115,6 +116,39 @@ function markDynamicUsage() {
|
|
|
115
116
|
function markRenderRequestApiUsage(kind) {
|
|
116
117
|
_getState().renderRequestApiUsage.add(kind);
|
|
117
118
|
}
|
|
119
|
+
async function runWithConnectionProbe(fn) {
|
|
120
|
+
const state = _getState();
|
|
121
|
+
const previousProbe = state.connectionProbe;
|
|
122
|
+
let interruptProbe = () => {};
|
|
123
|
+
const interrupted = new Promise((resolve) => {
|
|
124
|
+
interruptProbe = () => resolve({ completed: false });
|
|
125
|
+
});
|
|
126
|
+
const probe = {
|
|
127
|
+
interrupted: false,
|
|
128
|
+
interrupt() {
|
|
129
|
+
if (probe.interrupted) return;
|
|
130
|
+
probe.interrupted = true;
|
|
131
|
+
interruptProbe();
|
|
132
|
+
},
|
|
133
|
+
pending: new Promise(() => {})
|
|
134
|
+
};
|
|
135
|
+
state.connectionProbe = probe;
|
|
136
|
+
try {
|
|
137
|
+
const completed = Promise.resolve().then(fn).then((result) => ({
|
|
138
|
+
completed: true,
|
|
139
|
+
result
|
|
140
|
+
}));
|
|
141
|
+
return await Promise.race([completed, interrupted]);
|
|
142
|
+
} finally {
|
|
143
|
+
state.connectionProbe = previousProbe;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function suspendConnectionProbe() {
|
|
147
|
+
const probe = _getState().connectionProbe;
|
|
148
|
+
if (!probe) return null;
|
|
149
|
+
probe.interrupt();
|
|
150
|
+
return probe.pending;
|
|
151
|
+
}
|
|
118
152
|
function peekRenderRequestApiUsage() {
|
|
119
153
|
return [..._getState().renderRequestApiUsage].sort();
|
|
120
154
|
}
|
|
@@ -260,6 +294,7 @@ function runWithHeadersContext(ctx, fn) {
|
|
|
260
294
|
uCtx.headersContext = ctx;
|
|
261
295
|
uCtx.dynamicUsageDetected = false;
|
|
262
296
|
uCtx.renderRequestApiUsage = /* @__PURE__ */ new Set();
|
|
297
|
+
uCtx.connectionProbe = null;
|
|
263
298
|
uCtx.pendingSetCookies = [];
|
|
264
299
|
uCtx.draftModeCookieHeader = null;
|
|
265
300
|
uCtx.phase = "render";
|
|
@@ -268,6 +303,7 @@ function runWithHeadersContext(ctx, fn) {
|
|
|
268
303
|
headersContext: ctx,
|
|
269
304
|
dynamicUsageDetected: false,
|
|
270
305
|
renderRequestApiUsage: /* @__PURE__ */ new Set(),
|
|
306
|
+
connectionProbe: null,
|
|
271
307
|
invalidDynamicUsageError: null,
|
|
272
308
|
pendingSetCookies: [],
|
|
273
309
|
draftModeCookieHeader: null,
|
|
@@ -690,4 +726,4 @@ var RequestCookies = class {
|
|
|
690
726
|
}
|
|
691
727
|
};
|
|
692
728
|
//#endregion
|
|
693
|
-
export { applyMiddlewareRequestHeaders, consumeDynamicUsage, consumeInvalidDynamicUsageError, consumeRenderRequestApiUsage, cookies, draftMode, getAndClearPendingCookies, getDraftModeCookieHeader, getHeadersAccessPhase, getHeadersContext, headers, headersContextFromRequest, isDraftModeRequest, markDynamicUsage, markRenderRequestApiUsage, peekDynamicUsage, peekRenderRequestApiUsage, runWithHeadersContext, setHeadersAccessPhase, setHeadersContext, throwIfInsideCacheScope };
|
|
729
|
+
export { applyMiddlewareRequestHeaders, consumeDynamicUsage, consumeInvalidDynamicUsageError, consumeRenderRequestApiUsage, cookies, draftMode, getAndClearPendingCookies, getDraftModeCookieHeader, getHeadersAccessPhase, getHeadersContext, headers, headersContextFromRequest, isDraftModeRequest, markDynamicUsage, markRenderRequestApiUsage, peekDynamicUsage, peekRenderRequestApiUsage, runWithConnectionProbe, runWithHeadersContext, setHeadersAccessPhase, setHeadersContext, suspendConnectionProbe, throwIfInsideCacheScope };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import ipaddr from "ipaddr.js";
|
|
2
2
|
//#region src/shims/image-config.ts
|
|
3
|
+
const globRegexCache = /* @__PURE__ */ new Map();
|
|
3
4
|
/**
|
|
4
5
|
* Convert a glob pattern (with `*` and `**`) to a RegExp.
|
|
5
6
|
*
|
|
@@ -14,6 +15,9 @@ import ipaddr from "ipaddr.js";
|
|
|
14
15
|
* Literal characters are escaped for regex safety.
|
|
15
16
|
*/
|
|
16
17
|
function globToRegex(pattern, separator) {
|
|
18
|
+
const key = `${separator}\0${pattern}`;
|
|
19
|
+
const cached = globRegexCache.get(key);
|
|
20
|
+
if (cached !== void 0) return cached;
|
|
17
21
|
let regexStr = "^";
|
|
18
22
|
const doubleStar = separator === "." ? ".+" : ".*";
|
|
19
23
|
const singleStar = separator === "." ? "[^.]+" : "[^/]+";
|
|
@@ -27,7 +31,9 @@ function globToRegex(pattern, separator) {
|
|
|
27
31
|
}
|
|
28
32
|
}
|
|
29
33
|
regexStr += "$";
|
|
30
|
-
|
|
34
|
+
const re = new RegExp(regexStr);
|
|
35
|
+
globRegexCache.set(key, re);
|
|
36
|
+
return re;
|
|
31
37
|
}
|
|
32
38
|
/**
|
|
33
39
|
* Check whether a URL matches a single remote pattern.
|
|
@@ -27,9 +27,12 @@ declare function getPagesRouterComponentsMap(): PagesRouterComponentsMap;
|
|
|
27
27
|
* Pages Router map when the href matches an App Router route. No-op when the
|
|
28
28
|
* manifest is absent, the URL is external, or no app route matches.
|
|
29
29
|
*
|
|
30
|
-
* `pathname` is the basePath-stripped path —
|
|
31
|
-
*
|
|
32
|
-
*
|
|
30
|
+
* `pathname` is the basePath-stripped, trailing-slash-stripped path —
|
|
31
|
+
* matching Next.js's `removeTrailingSlash(removeBasePath(pathname))` key used
|
|
32
|
+
* at read time (router.ts:1442). Stripping here ensures the write and read
|
|
33
|
+
* keys agree regardless of whether the caller normalised trailing slashes
|
|
34
|
+
* first (e.g. `link.tsx` normalises to match `trailingSlash` config before
|
|
35
|
+
* calling, while `router.prefetch()` passes the raw user-supplied URL).
|
|
33
36
|
*/
|
|
34
37
|
declare function markAppRouteDetectedOnPrefetch(href: string, basePath: string): void;
|
|
35
38
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { stripBasePath } from "../../utils/base-path.js";
|
|
1
|
+
import { removeTrailingSlash, stripBasePath } from "../../utils/base-path.js";
|
|
2
2
|
import { createRouteTrieCache, matchRouteWithTrie } from "../../routing/route-matching.js";
|
|
3
3
|
//#region src/shims/internal/app-route-detection.ts
|
|
4
4
|
const appRouteTrieCache = createRouteTrieCache();
|
|
@@ -52,15 +52,19 @@ function matchesAppRoute(href, basePath) {
|
|
|
52
52
|
* Pages Router map when the href matches an App Router route. No-op when the
|
|
53
53
|
* manifest is absent, the URL is external, or no app route matches.
|
|
54
54
|
*
|
|
55
|
-
* `pathname` is the basePath-stripped path —
|
|
56
|
-
*
|
|
57
|
-
*
|
|
55
|
+
* `pathname` is the basePath-stripped, trailing-slash-stripped path —
|
|
56
|
+
* matching Next.js's `removeTrailingSlash(removeBasePath(pathname))` key used
|
|
57
|
+
* at read time (router.ts:1442). Stripping here ensures the write and read
|
|
58
|
+
* keys agree regardless of whether the caller normalised trailing slashes
|
|
59
|
+
* first (e.g. `link.tsx` normalises to match `trailingSlash` config before
|
|
60
|
+
* calling, while `router.prefetch()` passes the raw user-supplied URL).
|
|
58
61
|
*/
|
|
59
62
|
function markAppRouteDetectedOnPrefetch(href, basePath) {
|
|
60
63
|
if (typeof window === "undefined") return;
|
|
61
64
|
if (!matchesAppRoute(href, basePath)) return;
|
|
62
|
-
const
|
|
63
|
-
if (
|
|
65
|
+
const rawPathname = resolveSameOriginPathname(href, basePath);
|
|
66
|
+
if (rawPathname === null) return;
|
|
67
|
+
const pathname = removeTrailingSlash(rawPathname);
|
|
64
68
|
getPagesRouterComponentsMap()[pathname] = { __appRouter: true };
|
|
65
69
|
}
|
|
66
70
|
//#endregion
|
|
@@ -16,6 +16,11 @@ type AppRouterInstance = {
|
|
|
16
16
|
push(href: string, options?: NavigateOptions): void;
|
|
17
17
|
replace(href: string, options?: NavigateOptions): void;
|
|
18
18
|
prefetch(href: string, options?: PrefetchOptions): void;
|
|
19
|
+
/**
|
|
20
|
+
* Perform an experimental gesture transition navigation.
|
|
21
|
+
* Only available when experimental.gestureTransition is enabled.
|
|
22
|
+
*/
|
|
23
|
+
experimental_gesturePush?(href: string, options?: NavigateOptions): void;
|
|
19
24
|
};
|
|
20
25
|
declare const AppRouterContext: React$1.Context<AppRouterInstance | null> | null;
|
|
21
26
|
declare const GlobalLayoutRouterContext: React$1.Context<unknown> | null;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//#region src/shims/internal/link-status-registry.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Link-status pending registry.
|
|
4
|
+
*
|
|
5
|
+
* Tracks the single <Link> that started the most recent App Router navigation
|
|
6
|
+
* so its `useLinkStatus()` pending state can be reset when a *different*
|
|
7
|
+
* navigation begins — a different <Link> click, `router.push`/`router.replace`,
|
|
8
|
+
* a form submission, shallow routing via raw `history.pushState`, or browser
|
|
9
|
+
* back/forward. Without this, a Link's pending indicator stays "sticky" after
|
|
10
|
+
* an interrupting navigation, because the Link's own completion handler is the
|
|
11
|
+
* only thing that would otherwise clear it.
|
|
12
|
+
*
|
|
13
|
+
* Mirrors Next.js's `linkForMostRecentNavigation` /
|
|
14
|
+
* `setLinkForCurrentNavigation` in
|
|
15
|
+
* packages/next/src/client/components/links.ts, adapted to vinext's per-<Link>
|
|
16
|
+
* React state model: instead of an optimistic-status dispatcher, we hold the
|
|
17
|
+
* link's `setPending` setter.
|
|
18
|
+
*/
|
|
19
|
+
type PendingLinkSetter = (pending: boolean) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Mark `setter` as the link that started the most recent navigation, resetting
|
|
22
|
+
* the previously-tracked link's pending state to idle so only the last-clicked
|
|
23
|
+
* link shows a pending state.
|
|
24
|
+
*/
|
|
25
|
+
declare function setLinkForCurrentNavigation(setter: PendingLinkSetter): void;
|
|
26
|
+
/**
|
|
27
|
+
* Stop tracking `setter` if it is the current navigation link. Called when a
|
|
28
|
+
* <Link> finishes its own navigation or unmounts so we never hold a stale
|
|
29
|
+
* reference to an unmounted component's setter.
|
|
30
|
+
*/
|
|
31
|
+
declare function clearLinkForCurrentNavigation(setter: PendingLinkSetter): void;
|
|
32
|
+
/**
|
|
33
|
+
* Reset any link that is currently showing a pending state. Invoked at the
|
|
34
|
+
* start of every App Router navigation so that navigations not initiated by the
|
|
35
|
+
* tracked link — `router.push`/`router.replace`, form submissions, shallow
|
|
36
|
+
* routing, and browser back/forward — clear a stale pending indicator. A
|
|
37
|
+
* link-initiated navigation registers itself first via
|
|
38
|
+
* `setLinkForCurrentNavigation`; the matching call here consumes that marker and
|
|
39
|
+
* keeps the link pending.
|
|
40
|
+
*/
|
|
41
|
+
declare function notifyLinkNavigationStart(): void;
|
|
42
|
+
//#endregion
|
|
43
|
+
export { PendingLinkSetter, clearLinkForCurrentNavigation, notifyLinkNavigationStart, setLinkForCurrentNavigation };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//#region src/shims/internal/link-status-registry.ts
|
|
2
|
+
let linkSetterForMostRecentNavigation = null;
|
|
3
|
+
let currentNavigationIsLinkInitiated = false;
|
|
4
|
+
/**
|
|
5
|
+
* Mark `setter` as the link that started the most recent navigation, resetting
|
|
6
|
+
* the previously-tracked link's pending state to idle so only the last-clicked
|
|
7
|
+
* link shows a pending state.
|
|
8
|
+
*/
|
|
9
|
+
function setLinkForCurrentNavigation(setter) {
|
|
10
|
+
if (linkSetterForMostRecentNavigation && linkSetterForMostRecentNavigation !== setter) linkSetterForMostRecentNavigation(false);
|
|
11
|
+
linkSetterForMostRecentNavigation = setter;
|
|
12
|
+
currentNavigationIsLinkInitiated = true;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Stop tracking `setter` if it is the current navigation link. Called when a
|
|
16
|
+
* <Link> finishes its own navigation or unmounts so we never hold a stale
|
|
17
|
+
* reference to an unmounted component's setter.
|
|
18
|
+
*/
|
|
19
|
+
function clearLinkForCurrentNavigation(setter) {
|
|
20
|
+
if (linkSetterForMostRecentNavigation === setter) linkSetterForMostRecentNavigation = null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Reset any link that is currently showing a pending state. Invoked at the
|
|
24
|
+
* start of every App Router navigation so that navigations not initiated by the
|
|
25
|
+
* tracked link — `router.push`/`router.replace`, form submissions, shallow
|
|
26
|
+
* routing, and browser back/forward — clear a stale pending indicator. A
|
|
27
|
+
* link-initiated navigation registers itself first via
|
|
28
|
+
* `setLinkForCurrentNavigation`; the matching call here consumes that marker and
|
|
29
|
+
* keeps the link pending.
|
|
30
|
+
*/
|
|
31
|
+
function notifyLinkNavigationStart() {
|
|
32
|
+
if (currentNavigationIsLinkInitiated) {
|
|
33
|
+
currentNavigationIsLinkInitiated = false;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (linkSetterForMostRecentNavigation) {
|
|
37
|
+
linkSetterForMostRecentNavigation(false);
|
|
38
|
+
linkSetterForMostRecentNavigation = null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { clearLinkForCurrentNavigation, notifyLinkNavigationStart, setLinkForCurrentNavigation };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region src/shims/internal/route-pattern-for-warning.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Side-effect-free accessor for the current render's route pattern, used by
|
|
4
|
+
* Next.js-parity diagnostics such as the Link shim's
|
|
5
|
+
* "Invalid href ... in page: '...'" `console.error`.
|
|
6
|
+
*
|
|
7
|
+
* Mirrors Next.js's `router.pathname`, which is the *route pattern* (e.g.
|
|
8
|
+
* `/posts/[id]`), not the resolved URL. During Pages Router SSR the route
|
|
9
|
+
* pattern lives on the request-scoped SSR context; the server-only
|
|
10
|
+
* `router-state.ts` publishes an accessor for it under a well-known
|
|
11
|
+
* `Symbol.for` handle. We read it through that handle rather than importing
|
|
12
|
+
* `router.ts` directly — importing `router.ts` would pull its browser-only
|
|
13
|
+
* `installWindowNext()` side effect into every consumer of the Link shim
|
|
14
|
+
* (including the App Router client bundle), clobbering `window.next.router`.
|
|
15
|
+
*
|
|
16
|
+
* On the client (or when no accessor is registered, e.g. App Router) we fall
|
|
17
|
+
* back to `window.location.pathname`, then to `"/"`.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Register the server-side route-pattern accessor. Called once by the
|
|
21
|
+
* server-only router-state module on import. Idempotent.
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
declare function registerRoutePatternForWarningAccessor(accessor: () => string | null): void;
|
|
25
|
+
declare function getCurrentRoutePathnameForWarning(): string;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { getCurrentRoutePathnameForWarning, registerRoutePatternForWarningAccessor };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region src/shims/internal/route-pattern-for-warning.ts
|
|
2
|
+
/**
|
|
3
|
+
* Side-effect-free accessor for the current render's route pattern, used by
|
|
4
|
+
* Next.js-parity diagnostics such as the Link shim's
|
|
5
|
+
* "Invalid href ... in page: '...'" `console.error`.
|
|
6
|
+
*
|
|
7
|
+
* Mirrors Next.js's `router.pathname`, which is the *route pattern* (e.g.
|
|
8
|
+
* `/posts/[id]`), not the resolved URL. During Pages Router SSR the route
|
|
9
|
+
* pattern lives on the request-scoped SSR context; the server-only
|
|
10
|
+
* `router-state.ts` publishes an accessor for it under a well-known
|
|
11
|
+
* `Symbol.for` handle. We read it through that handle rather than importing
|
|
12
|
+
* `router.ts` directly — importing `router.ts` would pull its browser-only
|
|
13
|
+
* `installWindowNext()` side effect into every consumer of the Link shim
|
|
14
|
+
* (including the App Router client bundle), clobbering `window.next.router`.
|
|
15
|
+
*
|
|
16
|
+
* On the client (or when no accessor is registered, e.g. App Router) we fall
|
|
17
|
+
* back to `window.location.pathname`, then to `"/"`.
|
|
18
|
+
*/
|
|
19
|
+
const ROUTE_PATTERN_FOR_WARNING_ACCESSOR_KEY = Symbol.for("vinext.router.routePatternForWarningAccessor");
|
|
20
|
+
/**
|
|
21
|
+
* Register the server-side route-pattern accessor. Called once by the
|
|
22
|
+
* server-only router-state module on import. Idempotent.
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
function registerRoutePatternForWarningAccessor(accessor) {
|
|
26
|
+
globalThis[ROUTE_PATTERN_FOR_WARNING_ACCESSOR_KEY] = accessor;
|
|
27
|
+
}
|
|
28
|
+
function getCurrentRoutePathnameForWarning() {
|
|
29
|
+
if (typeof window === "undefined") {
|
|
30
|
+
const accessor = globalThis[ROUTE_PATTERN_FOR_WARNING_ACCESSOR_KEY];
|
|
31
|
+
if (accessor) try {
|
|
32
|
+
const pattern = accessor();
|
|
33
|
+
if (pattern) return pattern;
|
|
34
|
+
} catch {}
|
|
35
|
+
return "/";
|
|
36
|
+
}
|
|
37
|
+
return window.location?.pathname ?? "/";
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { getCurrentRoutePathnameForWarning, registerRoutePatternForWarningAccessor };
|
package/dist/shims/link.js
CHANGED
|
@@ -12,12 +12,14 @@ import { markAppRouteDetectedOnPrefetch } from "./internal/app-route-detection.j
|
|
|
12
12
|
import { isAbsoluteOrProtocolRelativeUrl, normalizePathTrailingSlash, resolveRelativeHref, toBrowserNavigationHref, toSameOriginAppPath, withBasePath } from "./url-utils.js";
|
|
13
13
|
import { appendSearchParamsToUrl, urlQueryToSearchParams } from "../utils/query.js";
|
|
14
14
|
import { getCurrentBrowserLocale } from "./client-locale.js";
|
|
15
|
+
import { getCurrentRoutePathnameForWarning } from "./internal/route-pattern-for-warning.js";
|
|
15
16
|
import { getNavigationRuntime, hasAppNavigationRuntime, registerNavigationRuntimeFunctions } from "../client/navigation-runtime.js";
|
|
16
17
|
import { createRscRequestHeaders, createRscRequestUrl, stripRscCacheBustingSearchParam, stripRscSuffix } from "../server/app-rsc-cache-busting.js";
|
|
17
18
|
import { getMountedSlotsHeader, getPrefetchCache, getPrefetchInterceptionContext, getPrefetchedUrls, hasPrefetchCacheEntryForNavigation, navigateClientSide, prefetchRscResponse } from "./navigation.js";
|
|
18
19
|
import { navigatePagesRouterLink } from "../client/pages-router-link-navigation.js";
|
|
19
20
|
import { getI18nContext } from "./i18n-context.js";
|
|
20
21
|
import { canLinkIntentPrefetch, canLinkPrefetch, getLinkPrefetchHref } from "./link-prefetch.js";
|
|
22
|
+
import { clearLinkForCurrentNavigation, notifyLinkNavigationStart, setLinkForCurrentNavigation } from "./internal/link-status-registry.js";
|
|
21
23
|
import React, { createContext, forwardRef, useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
22
24
|
import { jsx } from "react/jsx-runtime";
|
|
23
25
|
//#region src/shims/link.tsx
|
|
@@ -37,6 +39,7 @@ const LinkStatusContext = createContext({ pending: false });
|
|
|
37
39
|
function useLinkStatus() {
|
|
38
40
|
return useContext(LinkStatusContext);
|
|
39
41
|
}
|
|
42
|
+
if (typeof window !== "undefined") registerNavigationRuntimeFunctions({ notifyLinkNavigationStart });
|
|
40
43
|
/** basePath from next.config.js, injected by the plugin at build time */
|
|
41
44
|
const __basePath = process.env.__NEXT_ROUTER_BASEPATH ?? "";
|
|
42
45
|
/** trailingSlash from next.config.js, injected by the plugin at build time */
|
|
@@ -45,7 +48,7 @@ const __prefetchInlining = process.env.__VINEXT_PREFETCH_INLINING === "true";
|
|
|
45
48
|
const linkPrefetchRouteTrieCache = createRouteTrieCache();
|
|
46
49
|
function resolveHref(href) {
|
|
47
50
|
if (typeof href === "string") return href;
|
|
48
|
-
let url = href.pathname ?? "
|
|
51
|
+
let url = href.pathname ?? "";
|
|
49
52
|
if (href.query) {
|
|
50
53
|
const params = urlQueryToSearchParams(href.query);
|
|
51
54
|
url = appendSearchParamsToUrl(url, params);
|
|
@@ -81,17 +84,19 @@ function normalizeRepeatedSlashes(url) {
|
|
|
81
84
|
* `resolveHref`. We mirror that behaviour (no dedup) for exact parity.
|
|
82
85
|
*
|
|
83
86
|
* Note: Next.js uses `router.pathname` (the route pattern, e.g.
|
|
84
|
-
* `/posts/[id]`) for the "in page" segment of the message.
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
87
|
+
* `/posts/[id]`) for the "in page" segment of the message. The Next.js
|
|
88
|
+
* compat test asserts this exact text (`in page: '/my/path/[name]'`), so we
|
|
89
|
+
* source it from the current render's route pattern via
|
|
90
|
+
* `getCurrentRoutePathnameForWarning()`: the Pages Router SSR context's route
|
|
91
|
+
* pattern on the server, `window.location.pathname` on the client, falling
|
|
92
|
+
* back to `"/"`.
|
|
88
93
|
*/
|
|
89
94
|
function warnAndNormalizeRepeatedSlashesInHref(urlAsString) {
|
|
90
95
|
if (urlAsString.startsWith("//")) return urlAsString;
|
|
91
96
|
const urlProtoMatch = urlAsString.match(/^[a-z][a-z0-9+.-]*:\/\//i);
|
|
92
97
|
const urlAsStringNoProto = urlProtoMatch ? urlAsString.slice(urlProtoMatch[0].length) : urlAsString;
|
|
93
98
|
if (!(urlAsStringNoProto.split("?", 1)[0] || "").match(/(\/\/|\\)/)) return urlAsString;
|
|
94
|
-
const pathname =
|
|
99
|
+
const pathname = getCurrentRoutePathnameForWarning();
|
|
95
100
|
console.error(`Invalid href '${urlAsString}' passed to next/router in page: '${pathname}'. Repeated forward-slashes (//) or backslashes \\ are not valid in the href.`);
|
|
96
101
|
const normalizedNoProto = normalizeRepeatedSlashes(urlAsStringNoProto);
|
|
97
102
|
return (urlProtoMatch ? urlProtoMatch[0] : "") + normalizedNoProto;
|
|
@@ -381,10 +386,16 @@ const Link = forwardRef(function Link({ href, as, replace = false, prefetch: pre
|
|
|
381
386
|
const fullHref = normalizePathTrailingSlash(withBasePath(normalizedHref, __basePath), __trailingSlash);
|
|
382
387
|
const [pending, setPending] = useState(false);
|
|
383
388
|
const mountedRef = useRef(true);
|
|
389
|
+
const setPendingRef = useRef(null);
|
|
390
|
+
if (setPendingRef.current === null) setPendingRef.current = (next) => {
|
|
391
|
+
if (mountedRef.current) setPending(next);
|
|
392
|
+
};
|
|
384
393
|
useEffect(() => {
|
|
385
394
|
mountedRef.current = true;
|
|
395
|
+
const setter = setPendingRef.current;
|
|
386
396
|
return () => {
|
|
387
397
|
mountedRef.current = false;
|
|
398
|
+
if (setter) clearLinkForCurrentNavigation(setter);
|
|
388
399
|
};
|
|
389
400
|
}, []);
|
|
390
401
|
const internalRef = useRef(null);
|
|
@@ -497,10 +508,13 @@ const Link = forwardRef(function Link({ href, as, replace = false, prefetch: pre
|
|
|
497
508
|
if (navEvent.defaultPrevented) return;
|
|
498
509
|
} catch {}
|
|
499
510
|
if (getNavigationRuntime()?.functions.navigate) {
|
|
511
|
+
const setter = setPendingRef.current;
|
|
512
|
+
if (setter) setLinkForCurrentNavigation(setter);
|
|
500
513
|
setPending(true);
|
|
501
514
|
React.startTransition(() => {
|
|
502
515
|
navigateClientSide(navigateHref, replace ? "replace" : "push", scroll, true).finally(() => {
|
|
503
516
|
if (mountedRef.current) setPending(false);
|
|
517
|
+
if (setter) clearLinkForCurrentNavigation(setter);
|
|
504
518
|
});
|
|
505
519
|
});
|
|
506
520
|
return;
|
package/dist/shims/metadata.d.ts
CHANGED
|
@@ -271,11 +271,15 @@ declare function resolveModuleMetadata(mod: Record<string, unknown>, params?: Re
|
|
|
271
271
|
type MetadataHeadProps = {
|
|
272
272
|
metadata: Metadata;
|
|
273
273
|
pathname?: string;
|
|
274
|
+
trailingSlash?: boolean;
|
|
274
275
|
};
|
|
275
|
-
declare function renderMetadataToHtml(metadata: Metadata, pathname?: string
|
|
276
|
+
declare function renderMetadataToHtml(metadata: Metadata, pathname?: string, options?: {
|
|
277
|
+
trailingSlash?: boolean;
|
|
278
|
+
}): string;
|
|
276
279
|
declare function MetadataHead({
|
|
277
280
|
metadata,
|
|
278
|
-
pathname
|
|
281
|
+
pathname,
|
|
282
|
+
trailingSlash
|
|
279
283
|
}: MetadataHeadProps): React.JSX.Element;
|
|
280
284
|
//#endregion
|
|
281
285
|
export { DEFAULT_VIEWPORT, Metadata, MetadataHead, MetadataMergeEntry, Viewport, ViewportHead, mergeMetadata, mergeMetadataEntries, mergeViewport, postProcessMetadata, renderMetadataToHtml, resolveModuleMetadata, resolveModuleViewport };
|
package/dist/shims/metadata.js
CHANGED
|
@@ -287,18 +287,35 @@ function formatResolvedMetadataUrl(url) {
|
|
|
287
287
|
if (url.pathname === "/" && url.search === "" && url.hash === "") return url.origin;
|
|
288
288
|
return url.href;
|
|
289
289
|
}
|
|
290
|
-
|
|
290
|
+
const TRAILING_SLASH_FILE_REGEX = /^(?:\/((?!\.well-known(?:\/.*)?)((?:[^/]+\/)*)([^/]+\.\w+)))(\/?|$)/i;
|
|
291
|
+
function resolveMetadataUrl(url, metadataBase, trailingSlash) {
|
|
291
292
|
const value = stringifyUrl(url);
|
|
292
|
-
if (
|
|
293
|
+
if (!metadataBase) return value;
|
|
293
294
|
try {
|
|
294
|
-
|
|
295
|
+
const isAbsolute = isAbsoluteOrProtocolRelativeUrl(value);
|
|
296
|
+
const composed = isAbsolute ? new URL(value, metadataBase) : new URL(joinMetadataPath(metadataBase.pathname, value), metadataBase);
|
|
297
|
+
if (isAbsolute && composed.origin !== metadataBase.origin) return value;
|
|
298
|
+
if (trailingSlash === true && composed.search === "") {
|
|
299
|
+
if (composed.pathname !== "/" && !composed.pathname.endsWith("/") && !TRAILING_SLASH_FILE_REGEX.test(composed.pathname)) composed.pathname += "/";
|
|
300
|
+
}
|
|
301
|
+
const result = formatResolvedMetadataUrl(composed);
|
|
302
|
+
if (trailingSlash === true && result === metadataBase.origin) return `${metadataBase.origin}/`;
|
|
303
|
+
return result;
|
|
295
304
|
} catch {
|
|
296
305
|
return value;
|
|
297
306
|
}
|
|
298
307
|
}
|
|
299
|
-
function resolveCanonicalUrl(url, metadataBase, pathname) {
|
|
300
|
-
if (url instanceof URL) return resolveMetadataUrl(url, metadataBase);
|
|
301
|
-
return resolveMetadataUrl(resolveRelativeMetadataUrl(url, pathname), metadataBase);
|
|
308
|
+
function resolveCanonicalUrl(url, metadataBase, pathname, trailingSlash) {
|
|
309
|
+
if (url instanceof URL) return resolveMetadataUrl(url, metadataBase, trailingSlash);
|
|
310
|
+
return resolveMetadataUrl(resolveRelativeMetadataUrl(url, pathname), metadataBase, trailingSlash);
|
|
311
|
+
}
|
|
312
|
+
function resolveAlternateUrl(url, metadataBase, pathname, trailingSlash) {
|
|
313
|
+
if (url instanceof URL) {
|
|
314
|
+
const resolvedUrl = new URL(pathname, url);
|
|
315
|
+
url.searchParams.forEach((value, key) => resolvedUrl.searchParams.set(key, value));
|
|
316
|
+
return resolveMetadataUrl(resolvedUrl, metadataBase, trailingSlash);
|
|
317
|
+
}
|
|
318
|
+
return resolveCanonicalUrl(url, metadataBase, pathname, trailingSlash);
|
|
302
319
|
}
|
|
303
320
|
function isSocialImageDescriptor(value) {
|
|
304
321
|
return typeof value === "object" && !(value instanceof URL);
|
|
@@ -359,13 +376,14 @@ function renderMetadataElementToHtml(node) {
|
|
|
359
376
|
default: return "";
|
|
360
377
|
}
|
|
361
378
|
}
|
|
362
|
-
function renderMetadataToHtml(metadata, pathname = "/") {
|
|
379
|
+
function renderMetadataToHtml(metadata, pathname = "/", options) {
|
|
363
380
|
return renderMetadataElementToHtml(MetadataHead({
|
|
364
381
|
metadata,
|
|
365
|
-
pathname
|
|
382
|
+
pathname,
|
|
383
|
+
trailingSlash: options?.trailingSlash
|
|
366
384
|
}));
|
|
367
385
|
}
|
|
368
|
-
function MetadataHead({ metadata, pathname = "/" }) {
|
|
386
|
+
function MetadataHead({ metadata, pathname = "/", trailingSlash }) {
|
|
369
387
|
const elements = [];
|
|
370
388
|
let key = 0;
|
|
371
389
|
const base = metadata.metadataBase;
|
|
@@ -474,7 +492,7 @@ function MetadataHead({ metadata, pathname = "/" }) {
|
|
|
474
492
|
}, key++));
|
|
475
493
|
if (og.url) elements.push(/* @__PURE__ */ jsx("meta", {
|
|
476
494
|
property: "og:url",
|
|
477
|
-
content:
|
|
495
|
+
content: resolveCanonicalUrl(og.url, base, pathname, trailingSlash)
|
|
478
496
|
}, key++));
|
|
479
497
|
if (og.siteName) elements.push(/* @__PURE__ */ jsx("meta", {
|
|
480
498
|
property: "og:site_name",
|
|
@@ -691,22 +709,22 @@ function MetadataHead({ metadata, pathname = "/" }) {
|
|
|
691
709
|
const alt = metadata.alternates;
|
|
692
710
|
if (alt.canonical) elements.push(/* @__PURE__ */ jsx("link", {
|
|
693
711
|
rel: "canonical",
|
|
694
|
-
href: resolveCanonicalUrl(alt.canonical, base, pathname)
|
|
712
|
+
href: resolveCanonicalUrl(alt.canonical, base, pathname, trailingSlash)
|
|
695
713
|
}, key++));
|
|
696
714
|
if (alt.languages) for (const [lang, href] of Object.entries(alt.languages)) elements.push(/* @__PURE__ */ jsx("link", {
|
|
697
715
|
rel: "alternate",
|
|
698
716
|
hrefLang: lang,
|
|
699
|
-
href:
|
|
717
|
+
href: resolveAlternateUrl(href, base, pathname, trailingSlash)
|
|
700
718
|
}, key++));
|
|
701
719
|
if (alt.media) for (const [media, href] of Object.entries(alt.media)) elements.push(/* @__PURE__ */ jsx("link", {
|
|
702
720
|
rel: "alternate",
|
|
703
721
|
media,
|
|
704
|
-
href:
|
|
722
|
+
href: resolveAlternateUrl(href, base, pathname, trailingSlash)
|
|
705
723
|
}, key++));
|
|
706
724
|
if (alt.types) for (const [type, href] of Object.entries(alt.types)) elements.push(/* @__PURE__ */ jsx("link", {
|
|
707
725
|
rel: "alternate",
|
|
708
726
|
type,
|
|
709
|
-
href:
|
|
727
|
+
href: resolveAlternateUrl(href, base, pathname, trailingSlash)
|
|
710
728
|
}, key++));
|
|
711
729
|
}
|
|
712
730
|
if (metadata.verification) {
|