vinext 0.1.5 → 0.1.7
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 -2
- package/dist/build/client-build-config.d.ts +4 -6
- package/dist/build/client-build-config.js +27 -7
- package/dist/build/google-fonts/find-font-files-in-css.d.ts +16 -0
- package/dist/build/google-fonts/find-font-files-in-css.js +28 -0
- package/dist/build/prerender.js +1 -2
- package/dist/build/report.d.ts +1 -2
- package/dist/build/report.js +2 -11
- package/dist/build/run-prerender.js +1 -1
- package/dist/check.js +3 -3
- package/dist/client/pages-router-link-navigation.d.ts +10 -0
- package/dist/client/pages-router-link-navigation.js +3 -3
- package/dist/deploy.js +3 -3
- package/dist/entries/app-rsc-entry.d.ts +2 -1
- package/dist/entries/app-rsc-entry.js +35 -9
- package/dist/entries/app-rsc-manifest.js +76 -46
- package/dist/entries/pages-server-entry.js +1 -0
- package/dist/index.js +116 -74
- package/dist/init.d.ts +5 -4
- package/dist/init.js +27 -8
- package/dist/plugins/fonts.js +3 -1
- package/dist/plugins/middleware-export-validation.d.ts +5 -0
- package/dist/plugins/middleware-export-validation.js +36 -0
- package/dist/plugins/og-asset-ownership.d.ts +27 -0
- package/dist/plugins/og-asset-ownership.js +285 -0
- package/dist/plugins/og-assets.js +28 -10
- package/dist/plugins/remove-console.js +1 -1
- package/dist/plugins/strip-server-exports.js +2 -1
- package/dist/server/accept-encoding.d.ts +29 -0
- package/dist/server/accept-encoding.js +95 -0
- package/dist/server/app-action-request.d.ts +4 -0
- package/dist/server/app-action-request.js +9 -0
- package/dist/server/app-bfcache-identity.d.ts +12 -2
- package/dist/server/app-bfcache-identity.js +52 -23
- package/dist/server/app-browser-entry.js +8 -5
- package/dist/server/app-browser-navigation-controller.js +1 -1
- package/dist/server/app-browser-rsc-redirect.d.ts +2 -1
- package/dist/server/app-browser-rsc-redirect.js +8 -1
- package/dist/server/app-browser-state.js +1 -1
- package/dist/server/app-browser-visible-commit.js +1 -1
- package/dist/server/app-history-state.d.ts +1 -2
- package/dist/server/app-history-state.js +1 -1
- package/dist/server/app-middleware.d.ts +1 -0
- package/dist/server/app-middleware.js +1 -0
- package/dist/server/app-page-dispatch.d.ts +2 -2
- package/dist/server/app-page-head.js +2 -2
- package/dist/server/app-page-method.js +1 -1
- package/dist/server/app-page-request.d.ts +7 -0
- package/dist/server/app-page-request.js +19 -1
- package/dist/server/app-page-stream.js +2 -1
- package/dist/server/app-prerender-endpoints.d.ts +3 -3
- package/dist/server/app-prerender-endpoints.js +3 -4
- package/dist/server/app-route-handler-cache.js +1 -1
- package/dist/server/app-route-handler-dispatch.js +2 -2
- package/dist/server/app-route-handler-execution.js +3 -2
- package/dist/server/app-route-handler-policy.d.ts +1 -1
- package/dist/server/app-route-handler-policy.js +1 -6
- package/dist/server/app-route-module-loader.d.ts +62 -14
- package/dist/server/app-route-module-loader.js +61 -10
- package/dist/server/app-rsc-errors.d.ts +12 -1
- package/dist/server/app-rsc-errors.js +22 -2
- package/dist/server/app-rsc-handler.d.ts +16 -14
- package/dist/server/app-rsc-handler.js +47 -44
- package/dist/server/app-rsc-response-finalizer.js +5 -1
- package/dist/server/app-rsc-route-matching.d.ts +9 -0
- package/dist/server/app-rsc-route-matching.js +13 -0
- package/dist/server/app-server-action-execution.js +1 -1
- package/dist/server/app-ssr-entry.js +9 -7
- package/dist/server/headers.d.ts +5 -1
- package/dist/server/headers.js +5 -1
- package/dist/server/metadata-route-response.d.ts +3 -3
- package/dist/server/middleware-runtime.d.ts +2 -1
- package/dist/server/middleware-runtime.js +19 -8
- package/dist/server/middleware.d.ts +2 -2
- package/dist/server/middleware.js +26 -11
- package/dist/server/pages-request-pipeline.d.ts +3 -1
- package/dist/server/pages-request-pipeline.js +4 -2
- package/dist/server/prod-server.d.ts +1 -1
- package/dist/server/prod-server.js +85 -72
- package/dist/shims/dynamic.js +1 -1
- package/dist/shims/font-google-base.d.ts +1 -0
- package/dist/shims/font-google-base.js +6 -21
- package/dist/shims/internal/interpolate-as.d.ts +25 -0
- package/dist/shims/internal/interpolate-as.js +196 -0
- package/dist/shims/link.js +24 -1
- package/dist/shims/navigation.js +1 -1
- package/dist/shims/navigation.react-server.js +1 -1
- package/dist/shims/router.d.ts +1 -3
- package/dist/shims/router.js +62 -18
- package/dist/typegen.js +2 -10
- package/dist/utils/project.d.ts +7 -1
- package/dist/utils/project.js +16 -2
- package/package.json +3 -3
|
@@ -28,7 +28,7 @@ declare function isProxyFile(filePath: string): boolean;
|
|
|
28
28
|
*/
|
|
29
29
|
declare function resolveMiddlewareHandler(mod: MiddlewareModule, filePath: string): (request: NextRequest, event: NextFetchEvent) => Response | undefined | void | Promise<Response | undefined | void>;
|
|
30
30
|
/**
|
|
31
|
-
* Find the proxy or middleware file
|
|
31
|
+
* Find the proxy or middleware file at the selected app/pages convention level.
|
|
32
32
|
* Checks for proxy.ts (Next.js 16) first, then falls back to middleware.ts.
|
|
33
33
|
* If middleware.ts is found, logs a deprecation warning.
|
|
34
34
|
*
|
|
@@ -44,7 +44,7 @@ declare function resolveMiddlewareHandler(mod: MiddlewareModule, filePath: strin
|
|
|
44
44
|
* @see https://github.com/vercel/next.js/blob/canary/packages/next/src/build/index.ts
|
|
45
45
|
* (search for "MIDDLEWARE_FILENAME" + "file convention is deprecated")
|
|
46
46
|
*/
|
|
47
|
-
declare function findMiddlewareFile(root: string, fileMatcher: ValidFileMatcher): string | null;
|
|
47
|
+
declare function findMiddlewareFile(root: string, fileMatcher: ValidFileMatcher, conventionDir?: string): string | null;
|
|
48
48
|
/**
|
|
49
49
|
* Load and execute middleware for a given request.
|
|
50
50
|
*
|
|
@@ -13,7 +13,8 @@ import path from "node:path";
|
|
|
13
13
|
* https://github.com/vercel/next.js/blob/canary/packages/next/src/build/templates/middleware.ts
|
|
14
14
|
*/
|
|
15
15
|
function isProxyFile(filePath) {
|
|
16
|
-
|
|
16
|
+
const base = path.basename(filePath);
|
|
17
|
+
return base === "proxy" || base.startsWith("proxy.");
|
|
17
18
|
}
|
|
18
19
|
/**
|
|
19
20
|
* Resolve the middleware/proxy handler function from a module's exports.
|
|
@@ -32,9 +33,8 @@ function resolveMiddlewareHandler(mod, filePath) {
|
|
|
32
33
|
isProxy: isProxyFile(filePath)
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
|
-
const MIDDLEWARE_LOCATIONS = ["", "src/"];
|
|
36
36
|
/**
|
|
37
|
-
* Find the proxy or middleware file
|
|
37
|
+
* Find the proxy or middleware file at the selected app/pages convention level.
|
|
38
38
|
* Checks for proxy.ts (Next.js 16) first, then falls back to middleware.ts.
|
|
39
39
|
* If middleware.ts is found, logs a deprecation warning.
|
|
40
40
|
*
|
|
@@ -50,18 +50,33 @@ const MIDDLEWARE_LOCATIONS = ["", "src/"];
|
|
|
50
50
|
* @see https://github.com/vercel/next.js/blob/canary/packages/next/src/build/index.ts
|
|
51
51
|
* (search for "MIDDLEWARE_FILENAME" + "file convention is deprecated")
|
|
52
52
|
*/
|
|
53
|
-
function findMiddlewareFile(root, fileMatcher) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
function findMiddlewareFile(root, fileMatcher, conventionDir = root) {
|
|
54
|
+
let proxyPath = null;
|
|
55
|
+
for (const ext of fileMatcher.dottedExtensions) {
|
|
56
|
+
const fullPath = path.join(conventionDir, `proxy${ext}`);
|
|
57
|
+
if (fs.existsSync(fullPath)) {
|
|
58
|
+
proxyPath = fullPath;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
57
61
|
}
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
let middlewarePath = null;
|
|
63
|
+
for (const ext of fileMatcher.dottedExtensions) {
|
|
64
|
+
const fullPath = path.join(conventionDir, `middleware${ext}`);
|
|
60
65
|
if (fs.existsSync(fullPath)) {
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
middlewarePath = fullPath;
|
|
67
|
+
break;
|
|
63
68
|
}
|
|
64
69
|
}
|
|
70
|
+
if (proxyPath && middlewarePath) {
|
|
71
|
+
const relativeProxyPath = `./${path.relative(root, proxyPath)}`;
|
|
72
|
+
const relativeMiddlewarePath = `./${path.relative(root, middlewarePath)}`;
|
|
73
|
+
throw new Error(`Both middleware file "${relativeMiddlewarePath}" and proxy file "${relativeProxyPath}" are detected. Please use "${relativeProxyPath}" only. Learn more: https://nextjs.org/docs/messages/middleware-to-proxy`);
|
|
74
|
+
}
|
|
75
|
+
if (proxyPath) return proxyPath;
|
|
76
|
+
if (middlewarePath) {
|
|
77
|
+
console.warn("The \"middleware\" file convention is deprecated. Please use \"proxy\" instead.\n\n To migrate automatically, run:\n npx @next/codemod@canary middleware-to-proxy .\n\n Learn more: https://nextjs.org/docs/messages/middleware-to-proxy");
|
|
78
|
+
return middlewarePath;
|
|
79
|
+
}
|
|
65
80
|
return null;
|
|
66
81
|
}
|
|
67
82
|
function isMiddlewareModule(value) {
|
|
@@ -106,7 +106,9 @@ type PagesPipelineResult = {
|
|
|
106
106
|
* ASSUMPTION: request already has internal headers filtered and basePath stripped.
|
|
107
107
|
* The adapter is responsible for that pre-processing before calling runPagesRequest.
|
|
108
108
|
* The adapter also handles: open-redirect guard, _next/static 404, image optimization,
|
|
109
|
-
* _next/data normalization
|
|
109
|
+
* _next/data normalization and classification: adapters must rewrite the data
|
|
110
|
+
* URL to its page pathname and set `isDataReq` (the source of truth here), Node
|
|
111
|
+
* decode/normalize/400, public-file serving.
|
|
110
112
|
* runPagesRequest receives a "clean" request with basePath-stripped URL.
|
|
111
113
|
*/
|
|
112
114
|
declare function runPagesRequest(request: Request, deps: PagesPipelineDeps): Promise<PagesPipelineResult>;
|
|
@@ -40,7 +40,9 @@ function wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath) {
|
|
|
40
40
|
* ASSUMPTION: request already has internal headers filtered and basePath stripped.
|
|
41
41
|
* The adapter is responsible for that pre-processing before calling runPagesRequest.
|
|
42
42
|
* The adapter also handles: open-redirect guard, _next/static 404, image optimization,
|
|
43
|
-
* _next/data normalization
|
|
43
|
+
* _next/data normalization and classification: adapters must rewrite the data
|
|
44
|
+
* URL to its page pathname and set `isDataReq` (the source of truth here), Node
|
|
45
|
+
* decode/normalize/400, public-file serving.
|
|
44
46
|
* runPagesRequest receives a "clean" request with basePath-stripped URL.
|
|
45
47
|
*/
|
|
46
48
|
async function runPagesRequest(request, deps) {
|
|
@@ -54,7 +56,7 @@ async function runPagesRequest(request, deps) {
|
|
|
54
56
|
hadBasePath
|
|
55
57
|
};
|
|
56
58
|
{
|
|
57
|
-
const trailingSlashRedirect = normalizeTrailingSlash(pathname, basePath, trailingSlash, search);
|
|
59
|
+
const trailingSlashRedirect = isDataReq ? null : normalizeTrailingSlash(pathname, basePath, trailingSlash, search);
|
|
58
60
|
if (trailingSlashRedirect) return {
|
|
59
61
|
type: "response",
|
|
60
62
|
response: trailingSlashRedirect
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { negotiateEncoding } from "./accept-encoding.js";
|
|
1
2
|
import { StaticFileCache } from "./static-file-cache.js";
|
|
2
3
|
import { resolveRequestHost, trustProxy, trustedHosts } from "./proxy-trust.js";
|
|
3
4
|
import { IncomingMessage, ServerResponse } from "node:http";
|
|
@@ -59,7 +60,6 @@ type ProdServerOptions = {
|
|
|
59
60
|
declare const COMPRESSIBLE_TYPES: Set<string>;
|
|
60
61
|
/** Minimum size threshold for compression (in bytes). Below this, compression overhead isn't worth it. */
|
|
61
62
|
declare const COMPRESS_THRESHOLD = 1024;
|
|
62
|
-
declare function negotiateEncoding(req: IncomingMessage): "zstd" | "br" | "gzip" | "deflate" | null;
|
|
63
63
|
/**
|
|
64
64
|
* Merge middleware headers and a Web Response's headers into a single
|
|
65
65
|
* record suitable for Node.js `res.writeHead()`. Uses `getSetCookie()`
|
|
@@ -19,6 +19,7 @@ import { readTrustedPrerenderRouteParamsFromHeaders, serializePrerenderRoutePara
|
|
|
19
19
|
import { computeClientRuntimeMetadata } from "../utils/client-runtime-metadata.js";
|
|
20
20
|
import { readPrerenderSecret } from "../build/server-manifest.js";
|
|
21
21
|
import { seedMemoryCacheFromPrerender } from "./seed-cache.js";
|
|
22
|
+
import { negotiateEncoding, parseAcceptedEncodings, selectContentEncoding } from "./accept-encoding.js";
|
|
22
23
|
import fs from "node:fs";
|
|
23
24
|
import path from "node:path";
|
|
24
25
|
import fs$1 from "node:fs/promises";
|
|
@@ -167,25 +168,6 @@ const COMPRESSIBLE_TYPES = new Set([
|
|
|
167
168
|
/** Minimum size threshold for compression (in bytes). Below this, compression overhead isn't worth it. */
|
|
168
169
|
const COMPRESS_THRESHOLD = 1024;
|
|
169
170
|
/**
|
|
170
|
-
* Parse the Accept-Encoding header and return the best supported encoding.
|
|
171
|
-
* Preference order: zstd > br > gzip > deflate > identity.
|
|
172
|
-
*
|
|
173
|
-
* zstd decompresses ~3-5x faster than brotli at similar compression ratios.
|
|
174
|
-
* Supported in Chrome 123+, Firefox 126+. Safari can decompress but doesn't
|
|
175
|
-
* send zstd in Accept-Encoding, so it transparently falls back to br/gzip.
|
|
176
|
-
*/
|
|
177
|
-
const HAS_ZSTD = typeof zlib.createZstdCompress === "function";
|
|
178
|
-
function negotiateEncoding(req) {
|
|
179
|
-
const accept = req.headers["accept-encoding"];
|
|
180
|
-
if (!accept || typeof accept !== "string") return null;
|
|
181
|
-
const lower = accept.toLowerCase();
|
|
182
|
-
if (HAS_ZSTD && lower.includes("zstd")) return "zstd";
|
|
183
|
-
if (lower.includes("br")) return "br";
|
|
184
|
-
if (lower.includes("gzip")) return "gzip";
|
|
185
|
-
if (lower.includes("deflate")) return "deflate";
|
|
186
|
-
return null;
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
171
|
* Create a compression stream for the given encoding.
|
|
190
172
|
*/
|
|
191
173
|
function createCompressor(encoding, mode = "default") {
|
|
@@ -265,6 +247,23 @@ function omitHeadersCaseInsensitive(headersRecord, targets) {
|
|
|
265
247
|
}
|
|
266
248
|
return filtered;
|
|
267
249
|
}
|
|
250
|
+
function mergeVaryHeader(headers, value) {
|
|
251
|
+
const merged = { ...headers };
|
|
252
|
+
const existingKey = Object.keys(merged).find((key) => key.toLowerCase() === "vary");
|
|
253
|
+
if (!existingKey) {
|
|
254
|
+
merged.Vary = value;
|
|
255
|
+
return merged;
|
|
256
|
+
}
|
|
257
|
+
const rawVary = merged[existingKey];
|
|
258
|
+
const existingVary = Array.isArray(rawVary) ? rawVary.join(", ") : rawVary;
|
|
259
|
+
if (existingVary.trim().length === 0) {
|
|
260
|
+
merged[existingKey] = value;
|
|
261
|
+
return merged;
|
|
262
|
+
}
|
|
263
|
+
const values = existingVary.split(",").map((entry) => entry.trim().toLowerCase());
|
|
264
|
+
if (!values.includes("*") && !values.includes(value.toLowerCase())) merged[existingKey] = `${existingVary}, ${value}`;
|
|
265
|
+
return merged;
|
|
266
|
+
}
|
|
268
267
|
function matchesIfNoneMatchHeader(ifNoneMatch, etag) {
|
|
269
268
|
if (!ifNoneMatch) return false;
|
|
270
269
|
if (ifNoneMatch === "*") return true;
|
|
@@ -318,33 +317,37 @@ function mergeWebResponse(middlewareHeaders, response, statusOverride) {
|
|
|
318
317
|
function sendCompressed(req, res, body, contentType, statusCode, extraHeaders = {}, compress = true, statusText) {
|
|
319
318
|
const buf = typeof body === "string" ? Buffer.from(body) : body;
|
|
320
319
|
const baseType = contentType.split(";")[0].trim();
|
|
321
|
-
const
|
|
320
|
+
const varyByEncoding = compress && COMPRESSIBLE_TYPES.has(baseType);
|
|
321
|
+
const encoding = compress ? negotiateEncoding(req) : "identity";
|
|
322
322
|
const headersWithoutBodyHeaders = omitHeadersCaseInsensitive(extraHeaders, OMIT_BODY_HEADERS);
|
|
323
|
-
const writeHead = (headers) => {
|
|
324
|
-
if (
|
|
325
|
-
else res.writeHead(
|
|
323
|
+
const writeHead = (headers, responseStatus = statusCode, responseStatusText = statusText) => {
|
|
324
|
+
if (responseStatusText) res.writeHead(responseStatus, responseStatusText, headers);
|
|
325
|
+
else res.writeHead(responseStatus, headers);
|
|
326
326
|
};
|
|
327
|
-
if (encoding &&
|
|
328
|
-
|
|
329
|
-
const rawVary = extraHeaders["Vary"] ?? extraHeaders["vary"];
|
|
330
|
-
const existingVary = Array.isArray(rawVary) ? rawVary.join(", ") : rawVary;
|
|
331
|
-
let varyValue;
|
|
332
|
-
if (existingVary) varyValue = existingVary.toLowerCase().includes("accept-encoding") ? existingVary : existingVary + ", Accept-Encoding";
|
|
333
|
-
else varyValue = "Accept-Encoding";
|
|
334
|
-
writeHead({
|
|
327
|
+
if (encoding !== "identity" && varyByEncoding && buf.length >= 1024) {
|
|
328
|
+
writeHead(mergeVaryHeader({
|
|
335
329
|
...headersWithoutBodyHeaders,
|
|
336
330
|
"Content-Type": contentType,
|
|
337
|
-
"Content-Encoding": encoding
|
|
338
|
-
|
|
339
|
-
|
|
331
|
+
"Content-Encoding": encoding
|
|
332
|
+
}, "Accept-Encoding"));
|
|
333
|
+
if (req.method === "HEAD") {
|
|
334
|
+
res.end();
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const compressor = createCompressor(encoding);
|
|
340
338
|
compressor.end(buf);
|
|
341
339
|
pipeline(compressor, res, () => {});
|
|
342
340
|
} else {
|
|
343
|
-
|
|
341
|
+
const identityHeaders = {
|
|
344
342
|
...headersWithoutBodyHeaders,
|
|
345
343
|
"Content-Type": contentType,
|
|
346
344
|
"Content-Length": String(buf.length)
|
|
347
|
-
}
|
|
345
|
+
};
|
|
346
|
+
writeHead(varyByEncoding ? mergeVaryHeader(identityHeaders, "Accept-Encoding") : identityHeaders);
|
|
347
|
+
if (req.method === "HEAD") {
|
|
348
|
+
res.end();
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
348
351
|
res.end(buf);
|
|
349
352
|
}
|
|
350
353
|
}
|
|
@@ -378,24 +381,35 @@ async function tryServeStatic(req, res, clientDir, pathname, compress, cache, ex
|
|
|
378
381
|
}
|
|
379
382
|
const entry = cache.lookup(lookupPath);
|
|
380
383
|
if (!entry) return false;
|
|
384
|
+
const rawAe = compress ? req.headers["accept-encoding"] : void 0;
|
|
385
|
+
const parsed = typeof rawAe === "string" ? parseAcceptedEncodings(rawAe) : void 0;
|
|
386
|
+
const availableVariants = [
|
|
387
|
+
...entry.zst ? ["zstd"] : [],
|
|
388
|
+
...entry.br ? ["br"] : [],
|
|
389
|
+
...entry.gz ? ["gzip"] : []
|
|
390
|
+
];
|
|
391
|
+
const variesByEncoding = compress && availableVariants.length > 0;
|
|
392
|
+
const selected = parsed ? selectContentEncoding(parsed, availableVariants) : "identity";
|
|
393
|
+
const variant = selected === "zstd" ? entry.zst : selected === "br" ? entry.br : selected === "gzip" ? entry.gz : entry.original;
|
|
381
394
|
const ifNoneMatch = req.headers["if-none-match"];
|
|
382
395
|
if (responseStatus === 200 && typeof ifNoneMatch === "string" && matchesIfNoneMatchHeader(ifNoneMatch, entry.etag)) {
|
|
383
|
-
|
|
396
|
+
const notModifiedHeaders = variesByEncoding ? mergeVaryHeader({
|
|
384
397
|
...entry.notModifiedHeaders,
|
|
385
398
|
...extraHeaders
|
|
386
|
-
})
|
|
387
|
-
|
|
399
|
+
}, "Accept-Encoding") : {
|
|
400
|
+
...entry.notModifiedHeaders,
|
|
401
|
+
...extraHeaders
|
|
402
|
+
};
|
|
403
|
+
if (selected !== "identity") notModifiedHeaders["Content-Encoding"] = selected;
|
|
404
|
+
res.writeHead(304, notModifiedHeaders);
|
|
388
405
|
res.end();
|
|
389
406
|
return true;
|
|
390
407
|
}
|
|
391
|
-
const
|
|
392
|
-
const ae = typeof rawAe === "string" ? rawAe.toLowerCase() : void 0;
|
|
393
|
-
const variant = ae ? ae.includes("zstd") && entry.zst || ae.includes("br") && entry.br || ae.includes("gzip") && entry.gz || entry.original : entry.original;
|
|
394
|
-
if (extraHeaders) res.writeHead(responseStatus, {
|
|
408
|
+
const responseHeaders = {
|
|
395
409
|
...variant.headers,
|
|
396
410
|
...extraHeaders
|
|
397
|
-
}
|
|
398
|
-
|
|
411
|
+
};
|
|
412
|
+
res.writeHead(responseStatus, variesByEncoding ? mergeVaryHeader(responseHeaders, "Accept-Encoding") : responseHeaders);
|
|
399
413
|
if (omitBody || req.method === "HEAD") {
|
|
400
414
|
res.end();
|
|
401
415
|
return true;
|
|
@@ -428,18 +442,6 @@ async function tryServeStatic(req, res, clientDir, pathname, compress, cache, ex
|
|
|
428
442
|
const etag = isHashed && etagFromFilenameHash(resolved.path, ext) || `W/"${resolved.size}-${Math.floor(resolved.mtimeMs / 1e3)}"`;
|
|
429
443
|
const baseType = ct.split(";")[0].trim();
|
|
430
444
|
const isCompressible = compress && COMPRESSIBLE_TYPES.has(baseType);
|
|
431
|
-
const ifNoneMatch = req.headers["if-none-match"];
|
|
432
|
-
if (responseStatus === 200 && typeof ifNoneMatch === "string" && matchesIfNoneMatchHeader(ifNoneMatch, etag)) {
|
|
433
|
-
const notModifiedHeaders = {
|
|
434
|
-
ETag: etag,
|
|
435
|
-
"Cache-Control": cacheControl,
|
|
436
|
-
...isCompressible ? { Vary: "Accept-Encoding" } : void 0,
|
|
437
|
-
...extraHeaders
|
|
438
|
-
};
|
|
439
|
-
res.writeHead(304, notModifiedHeaders);
|
|
440
|
-
res.end();
|
|
441
|
-
return true;
|
|
442
|
-
}
|
|
443
445
|
const baseHeaders = {
|
|
444
446
|
"Content-Type": ct,
|
|
445
447
|
"Cache-Control": cacheControl,
|
|
@@ -448,12 +450,19 @@ async function tryServeStatic(req, res, clientDir, pathname, compress, cache, ex
|
|
|
448
450
|
};
|
|
449
451
|
if (isCompressible) {
|
|
450
452
|
const encoding = negotiateEncoding(req);
|
|
451
|
-
|
|
452
|
-
|
|
453
|
+
const ifNoneMatch = req.headers["if-none-match"];
|
|
454
|
+
if (responseStatus === 200 && typeof ifNoneMatch === "string" && matchesIfNoneMatchHeader(ifNoneMatch, etag)) {
|
|
455
|
+
const notModifiedHeaders = mergeVaryHeader(baseHeaders, "Accept-Encoding");
|
|
456
|
+
if (encoding !== "identity") notModifiedHeaders["Content-Encoding"] = encoding;
|
|
457
|
+
res.writeHead(304, notModifiedHeaders);
|
|
458
|
+
res.end();
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
if (encoding !== "identity") {
|
|
462
|
+
res.writeHead(responseStatus, mergeVaryHeader({
|
|
453
463
|
...baseHeaders,
|
|
454
|
-
"Content-Encoding": encoding
|
|
455
|
-
|
|
456
|
-
});
|
|
464
|
+
"Content-Encoding": encoding
|
|
465
|
+
}, "Accept-Encoding"));
|
|
457
466
|
if (omitBody || req.method === "HEAD") {
|
|
458
467
|
res.end();
|
|
459
468
|
return true;
|
|
@@ -468,10 +477,17 @@ async function tryServeStatic(req, res, clientDir, pathname, compress, cache, ex
|
|
|
468
477
|
return true;
|
|
469
478
|
}
|
|
470
479
|
}
|
|
471
|
-
|
|
480
|
+
const ifNoneMatch = req.headers["if-none-match"];
|
|
481
|
+
if (responseStatus === 200 && typeof ifNoneMatch === "string" && matchesIfNoneMatchHeader(ifNoneMatch, etag)) {
|
|
482
|
+
res.writeHead(304, isCompressible ? mergeVaryHeader(baseHeaders, "Accept-Encoding") : baseHeaders);
|
|
483
|
+
res.end();
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
const identityHeaders = {
|
|
472
487
|
...baseHeaders,
|
|
473
488
|
"Content-Length": String(resolved.size)
|
|
474
|
-
}
|
|
489
|
+
};
|
|
490
|
+
res.writeHead(responseStatus, isCompressible ? mergeVaryHeader(identityHeaders, "Accept-Encoding") : identityHeaders);
|
|
475
491
|
if (omitBody || req.method === "HEAD") {
|
|
476
492
|
res.end();
|
|
477
493
|
return true;
|
|
@@ -568,25 +584,22 @@ async function sendWebResponse(webResponse, req, res, compress) {
|
|
|
568
584
|
if (existing !== void 0) nodeHeaders[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
|
|
569
585
|
else nodeHeaders[key] = value;
|
|
570
586
|
});
|
|
587
|
+
const alreadyEncoded = webResponse.headers.get("content-encoding") !== null;
|
|
571
588
|
if (!webResponse.body) {
|
|
572
589
|
writeHead(nodeHeaders);
|
|
573
590
|
res.end();
|
|
574
591
|
return;
|
|
575
592
|
}
|
|
576
|
-
const alreadyEncoded = webResponse.headers.has("content-encoding");
|
|
577
593
|
const baseType = (webResponse.headers.get("content-type") ?? "").split(";")[0].trim();
|
|
578
|
-
const
|
|
579
|
-
const
|
|
594
|
+
const varyByEncoding = compress && !alreadyEncoded && COMPRESSIBLE_TYPES.has(baseType);
|
|
595
|
+
const encoding = compress && !alreadyEncoded ? negotiateEncoding(req) : "identity";
|
|
596
|
+
const shouldCompress = encoding !== "identity" && COMPRESSIBLE_TYPES.has(baseType);
|
|
580
597
|
if (shouldCompress) {
|
|
581
598
|
delete nodeHeaders["content-length"];
|
|
582
599
|
delete nodeHeaders["Content-Length"];
|
|
583
600
|
nodeHeaders["Content-Encoding"] = encoding;
|
|
584
|
-
const existingVary = nodeHeaders["Vary"] ?? nodeHeaders["vary"];
|
|
585
|
-
if (existingVary) {
|
|
586
|
-
if (!String(existingVary).toLowerCase().includes("accept-encoding")) nodeHeaders["Vary"] = existingVary + ", Accept-Encoding";
|
|
587
|
-
} else nodeHeaders["Vary"] = "Accept-Encoding";
|
|
588
601
|
}
|
|
589
|
-
writeHead(nodeHeaders);
|
|
602
|
+
writeHead(varyByEncoding ? mergeVaryHeader(nodeHeaders, "Accept-Encoding") : nodeHeaders);
|
|
590
603
|
if (req.method === "HEAD") {
|
|
591
604
|
cancelResponseBody(webResponse);
|
|
592
605
|
res.end();
|
package/dist/shims/dynamic.js
CHANGED
|
@@ -127,7 +127,7 @@ function dynamic(dynamicInput, options) {
|
|
|
127
127
|
const preloadModuleIds = loadableGenerated?.modules ?? modules;
|
|
128
128
|
if (!ssr) {
|
|
129
129
|
if (isServer) {
|
|
130
|
-
const SSRFalse = (_props) => LoadingComponent ? React.createElement(LoadingComponent, createDynamicLoadingProps(
|
|
130
|
+
const SSRFalse = (_props) => LoadingComponent ? React.createElement(LoadingComponent, createDynamicLoadingProps()) : null;
|
|
131
131
|
SSRFalse.displayName = "DynamicSSRFalse";
|
|
132
132
|
return SSRFalse;
|
|
133
133
|
}
|
|
@@ -206,27 +206,12 @@ function getSSRFontPreloads() {
|
|
|
206
206
|
return [...ssrFontPreloads];
|
|
207
207
|
}
|
|
208
208
|
/**
|
|
209
|
-
*
|
|
210
|
-
* Parses url('...') references from the CSS text.
|
|
211
|
-
*/
|
|
212
|
-
function extractFontUrlsFromCSS(css) {
|
|
213
|
-
const urls = [];
|
|
214
|
-
const urlRegex = /url\(['"]?([^'")]+)['"]?\)/g;
|
|
215
|
-
let match;
|
|
216
|
-
while ((match = urlRegex.exec(css)) !== null) {
|
|
217
|
-
const url = match[1];
|
|
218
|
-
if (url && url.startsWith("/")) urls.push(url);
|
|
219
|
-
}
|
|
220
|
-
return urls;
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* Collect font file URLs from self-hosted CSS for preload link generation.
|
|
209
|
+
* Collect build-selected font file URLs for preload link generation.
|
|
224
210
|
* Only collects on the server (SSR). Deduplicates by href using a Set for O(1) lookups.
|
|
225
211
|
*/
|
|
226
|
-
function
|
|
212
|
+
function collectFontPreloads(urls) {
|
|
227
213
|
if (typeof document !== "undefined") return;
|
|
228
|
-
const urls
|
|
229
|
-
for (const href of urls) if (!ssrFontPreloadHrefs.has(href)) {
|
|
214
|
+
for (const href of urls) if (href.startsWith("/") && !ssrFontPreloadHrefs.has(href)) {
|
|
230
215
|
ssrFontPreloadHrefs.add(href);
|
|
231
216
|
ssrFontPreloads.push({
|
|
232
217
|
href,
|
|
@@ -240,10 +225,10 @@ const injectedSelfHosted = /* @__PURE__ */ new Set();
|
|
|
240
225
|
* Inject self-hosted @font-face CSS (from the build plugin).
|
|
241
226
|
* This replaces the CDN <link> tag with inline CSS.
|
|
242
227
|
*/
|
|
243
|
-
function injectSelfHostedCSS(css) {
|
|
228
|
+
function injectSelfHostedCSS(css, preloadUrls = []) {
|
|
229
|
+
collectFontPreloads(preloadUrls);
|
|
244
230
|
if (injectedSelfHosted.has(css)) return;
|
|
245
231
|
injectedSelfHosted.add(css);
|
|
246
|
-
collectFontPreloadsFromCSS(css);
|
|
247
232
|
if (typeof document === "undefined") {
|
|
248
233
|
ssrFontStyles.push(css);
|
|
249
234
|
return;
|
|
@@ -277,7 +262,7 @@ function createFontLoader(family) {
|
|
|
277
262
|
internalStyle: internal?.fontStyle,
|
|
278
263
|
google: true
|
|
279
264
|
});
|
|
280
|
-
if (internal?.selfHostedCSS) injectSelfHostedCSS(internal.selfHostedCSS);
|
|
265
|
+
if (internal?.selfHostedCSS) injectSelfHostedCSS(internal.selfHostedCSS, internal.preloadUrls);
|
|
281
266
|
else {
|
|
282
267
|
const url = buildGoogleFontsUrl(family, options);
|
|
283
268
|
injectFontStylesheet(url);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { UrlQuery } from "../../utils/query.js";
|
|
2
|
+
|
|
3
|
+
//#region src/shims/internal/interpolate-as.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Wire-compatible alias for Node's `querystring.ParsedUrlQuery`. Inlined here
|
|
6
|
+
* so this module has no dependency on the `querystring` types package.
|
|
7
|
+
*/
|
|
8
|
+
type ParsedUrlQuery = {
|
|
9
|
+
[key: string]: string | string[] | undefined;
|
|
10
|
+
};
|
|
11
|
+
type DynamicRouteHrefProjection = {
|
|
12
|
+
href: string;
|
|
13
|
+
params: string[];
|
|
14
|
+
query: ParsedUrlQuery;
|
|
15
|
+
routePathname: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Resolve a bracket-pattern route href against its displayed href. Query
|
|
19
|
+
* values can be supplied directly (object-form hrefs) or parsed from the route
|
|
20
|
+
* href (string-form hrefs). A `?` after `#` is part of the fragment, not a
|
|
21
|
+
* query delimiter.
|
|
22
|
+
*/
|
|
23
|
+
declare function interpolateDynamicRouteHref(routeHref: string, asHref: string, queryInput?: UrlQuery): DynamicRouteHrefProjection | null;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { DynamicRouteHrefProjection, interpolateDynamicRouteHref };
|