vinext 0.0.22 → 0.0.24
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/build/static-export.d.ts.map +1 -1
- package/dist/build/static-export.js +9 -7
- package/dist/build/static-export.js.map +1 -1
- package/dist/config/config-matchers.d.ts.map +1 -1
- package/dist/config/config-matchers.js +13 -3
- package/dist/config/config-matchers.js.map +1 -1
- package/dist/config/next-config.d.ts +4 -1
- package/dist/config/next-config.d.ts.map +1 -1
- package/dist/config/next-config.js +10 -5
- package/dist/config/next-config.js.map +1 -1
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +83 -24
- package/dist/deploy.js.map +1 -1
- package/dist/index.d.ts +36 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +230 -40
- package/dist/index.js.map +1 -1
- package/dist/routing/app-router.d.ts +2 -1
- package/dist/routing/app-router.d.ts.map +1 -1
- package/dist/routing/app-router.js +73 -66
- package/dist/routing/app-router.js.map +1 -1
- package/dist/routing/file-matcher.d.ts +24 -0
- package/dist/routing/file-matcher.d.ts.map +1 -0
- package/dist/routing/file-matcher.js +75 -0
- package/dist/routing/file-matcher.js.map +1 -0
- package/dist/routing/pages-router.d.ts +3 -2
- package/dist/routing/pages-router.d.ts.map +1 -1
- package/dist/routing/pages-router.js +24 -17
- package/dist/routing/pages-router.js.map +1 -1
- package/dist/server/app-dev-server.d.ts.map +1 -1
- package/dist/server/app-dev-server.js +110 -64
- package/dist/server/app-dev-server.js.map +1 -1
- package/dist/server/dev-server.d.ts +2 -1
- package/dist/server/dev-server.d.ts.map +1 -1
- package/dist/server/dev-server.js +16 -14
- package/dist/server/dev-server.js.map +1 -1
- package/dist/server/prod-server.d.ts +8 -2
- package/dist/server/prod-server.d.ts.map +1 -1
- package/dist/server/prod-server.js +71 -16
- package/dist/server/prod-server.js.map +1 -1
- package/dist/server/worker-utils.d.ts +15 -0
- package/dist/server/worker-utils.d.ts.map +1 -0
- package/dist/server/worker-utils.js +41 -0
- package/dist/server/worker-utils.js.map +1 -0
- package/dist/shims/cache.d.ts +1 -1
- package/dist/shims/cache.d.ts.map +1 -1
- package/dist/shims/cache.js +8 -3
- package/dist/shims/cache.js.map +1 -1
- package/dist/shims/headers.d.ts +6 -0
- package/dist/shims/headers.d.ts.map +1 -1
- package/dist/shims/headers.js +8 -0
- package/dist/shims/headers.js.map +1 -1
- package/dist/shims/metadata.d.ts +1 -0
- package/dist/shims/metadata.d.ts.map +1 -1
- package/dist/shims/metadata.js +5 -1
- package/dist/shims/metadata.js.map +1 -1
- package/dist/utils/project.d.ts +13 -1
- package/dist/utils/project.d.ts.map +1 -1
- package/dist/utils/project.js +63 -13
- package/dist/utils/project.js.map +1 -1
- package/package.json +6 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { loadEnv, parseAst } from "vite";
|
|
2
2
|
import { pagesRouter, apiRouter, invalidateRouteCache, matchRoute, patternToNextFormat as pagesPatternToNextFormat } from "./routing/pages-router.js";
|
|
3
3
|
import { appRouter, invalidateAppRouteCache } from "./routing/app-router.js";
|
|
4
|
+
import { createValidFileMatcher } from "./routing/file-matcher.js";
|
|
4
5
|
import { createSSRHandler } from "./server/dev-server.js";
|
|
5
6
|
import { handleApiRoute } from "./server/api-handler.js";
|
|
6
7
|
import { generateRscEntry, generateSsrEntry, generateBrowserEntry, } from "./server/app-dev-server.js";
|
|
@@ -10,11 +11,13 @@ import { logRequest, now } from "./server/request-log.js";
|
|
|
10
11
|
import { generateSafeRegExpCode, generateMiddlewareMatcherCode, generateNormalizePathCode } from "./server/middleware-codegen.js";
|
|
11
12
|
import { normalizePath } from "./server/normalize-path.js";
|
|
12
13
|
import { findInstrumentationFile, runInstrumentation } from "./server/instrumentation.js";
|
|
14
|
+
import { PHASE_PRODUCTION_BUILD, PHASE_DEVELOPMENT_SERVER } from "./shims/constants.js";
|
|
13
15
|
import { validateDevRequest } from "./server/dev-origin-check.js";
|
|
14
16
|
import { safeRegExp, isExternalUrl, proxyExternalRequest, parseCookies, matchHeaders, matchRedirect, matchRewrite, } from "./config/config-matchers.js";
|
|
15
17
|
import { scanMetadataFiles } from "./server/metadata-routes.js";
|
|
16
18
|
import { detectPackageManager } from "./utils/project.js";
|
|
17
19
|
import tsconfigPaths from "vite-tsconfig-paths";
|
|
20
|
+
import react from "@vitejs/plugin-react";
|
|
18
21
|
import MagicString from "magic-string";
|
|
19
22
|
import path from "node:path";
|
|
20
23
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -493,6 +496,7 @@ export default function vinext(options = {}) {
|
|
|
493
496
|
let hasAppDir = false;
|
|
494
497
|
let hasPagesDir = false;
|
|
495
498
|
let nextConfig;
|
|
499
|
+
let fileMatcher;
|
|
496
500
|
let middlewarePath = null;
|
|
497
501
|
let instrumentationPath = null;
|
|
498
502
|
let hasCloudflarePlugin = false;
|
|
@@ -506,8 +510,8 @@ export default function vinext(options = {}) {
|
|
|
506
510
|
* This is the entry point for `vite build --ssr`.
|
|
507
511
|
*/
|
|
508
512
|
async function generateServerEntry() {
|
|
509
|
-
const pageRoutes = await pagesRouter(pagesDir);
|
|
510
|
-
const apiRoutes = await apiRouter(pagesDir);
|
|
513
|
+
const pageRoutes = await pagesRouter(pagesDir, nextConfig?.pageExtensions, fileMatcher);
|
|
514
|
+
const apiRoutes = await apiRouter(pagesDir, nextConfig?.pageExtensions, fileMatcher);
|
|
511
515
|
// Generate import statements using absolute paths since virtual
|
|
512
516
|
// modules don't have a real file location for relative resolution.
|
|
513
517
|
const pageImports = pageRoutes.map((r, i) => {
|
|
@@ -527,16 +531,15 @@ export default function vinext(options = {}) {
|
|
|
527
531
|
return ` { pattern: ${JSON.stringify(r.pattern)}, isDynamic: ${r.isDynamic}, params: ${JSON.stringify(r.params)}, module: api_${i} }`;
|
|
528
532
|
});
|
|
529
533
|
// Check for _app and _document
|
|
530
|
-
const
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
const
|
|
534
|
-
const docFileBase = path.join(pagesDir, "_document").replace(/\\/g, "/");
|
|
534
|
+
const appFilePath = findFileWithExts(pagesDir, "_app", fileMatcher);
|
|
535
|
+
const docFilePath = findFileWithExts(pagesDir, "_document", fileMatcher);
|
|
536
|
+
const hasApp = appFilePath !== null;
|
|
537
|
+
const hasDoc = docFilePath !== null;
|
|
535
538
|
const appImportCode = hasApp
|
|
536
|
-
? `import { default as AppComponent } from ${JSON.stringify(
|
|
539
|
+
? `import { default as AppComponent } from ${JSON.stringify(appFilePath.replace(/\\/g, "/"))};`
|
|
537
540
|
: `const AppComponent = null;`;
|
|
538
541
|
const docImportCode = hasDoc
|
|
539
|
-
? `import { default as DocumentComponent } from ${JSON.stringify(
|
|
542
|
+
? `import { default as DocumentComponent } from ${JSON.stringify(docFilePath.replace(/\\/g, "/"))};`
|
|
540
543
|
: `const DocumentComponent = null;`;
|
|
541
544
|
// Serialize i18n config for embedding in the server entry
|
|
542
545
|
const i18nConfigJson = nextConfig?.i18n
|
|
@@ -1460,8 +1463,9 @@ ${middlewareExportCode}
|
|
|
1460
1463
|
* __NEXT_DATA__ to determine which page to hydrate.
|
|
1461
1464
|
*/
|
|
1462
1465
|
async function generateClientEntry() {
|
|
1463
|
-
const pageRoutes = await pagesRouter(pagesDir);
|
|
1464
|
-
const
|
|
1466
|
+
const pageRoutes = await pagesRouter(pagesDir, nextConfig?.pageExtensions, fileMatcher);
|
|
1467
|
+
const appFilePath = findFileWithExts(pagesDir, "_app", fileMatcher);
|
|
1468
|
+
const hasApp = appFilePath !== null;
|
|
1465
1469
|
// Build a map of route pattern -> dynamic import.
|
|
1466
1470
|
// Keys must use Next.js bracket format (e.g. "/user/[id]") to match
|
|
1467
1471
|
// __NEXT_DATA__.page which is set via patternToNextFormat() during SSR.
|
|
@@ -1473,7 +1477,7 @@ ${middlewareExportCode}
|
|
|
1473
1477
|
// lgtm[js/bad-code-sanitization]
|
|
1474
1478
|
return ` ${JSON.stringify(nextFormatPattern)}: () => import(${JSON.stringify(absPath)})`;
|
|
1475
1479
|
});
|
|
1476
|
-
const appFileBase =
|
|
1480
|
+
const appFileBase = appFilePath?.replace(/\\/g, "/");
|
|
1477
1481
|
return `
|
|
1478
1482
|
import React from "react";
|
|
1479
1483
|
import { hydrateRoot } from "react-dom/client";
|
|
@@ -1594,10 +1598,15 @@ hydrate();
|
|
|
1594
1598
|
// Shared state for the MDX proxy plugin. Populated during config() if MDX
|
|
1595
1599
|
// files are detected and @mdx-js/rollup is installed.
|
|
1596
1600
|
let mdxDelegate = null;
|
|
1601
|
+
const reactPlugin = options.react === false
|
|
1602
|
+
? false
|
|
1603
|
+
: react(options.react === true ? undefined : options.react);
|
|
1597
1604
|
const plugins = [
|
|
1598
1605
|
// Resolve tsconfig paths/baseUrl aliases so real-world Next.js repos
|
|
1599
1606
|
// that use @/*, #/*, or baseUrl imports work out of the box.
|
|
1600
1607
|
tsconfigPaths(),
|
|
1608
|
+
// React Fast Refresh + JSX transform for client components.
|
|
1609
|
+
reactPlugin,
|
|
1601
1610
|
// Transform CJS require()/module.exports to ESM before other plugins
|
|
1602
1611
|
// analyze imports (RSC directive scanning, shim resolution, etc.)
|
|
1603
1612
|
commonjs(),
|
|
@@ -1666,8 +1675,10 @@ hydrate();
|
|
|
1666
1675
|
middlewarePath = findMiddlewareFile(root);
|
|
1667
1676
|
instrumentationPath = findInstrumentationFile(root);
|
|
1668
1677
|
// Load next.config.js if present (always from project root, not src/)
|
|
1669
|
-
const
|
|
1678
|
+
const phase = env?.command === "build" ? PHASE_PRODUCTION_BUILD : PHASE_DEVELOPMENT_SERVER;
|
|
1679
|
+
const rawConfig = await loadNextConfig(root, phase);
|
|
1670
1680
|
nextConfig = await resolveNextConfig(rawConfig);
|
|
1681
|
+
fileMatcher = createValidFileMatcher(nextConfig.pageExtensions);
|
|
1671
1682
|
// Merge env from next.config.js with NEXT_PUBLIC_* env vars
|
|
1672
1683
|
const defines = getNextPublicEnvDefines();
|
|
1673
1684
|
if (!config.define ||
|
|
@@ -1976,6 +1987,13 @@ hydrate();
|
|
|
1976
1987
|
},
|
|
1977
1988
|
},
|
|
1978
1989
|
client: {
|
|
1990
|
+
// Explicitly mark as client consumer so other plugins (e.g. Nitro)
|
|
1991
|
+
// can detect this during configEnvironment hooks — before Vite
|
|
1992
|
+
// applies the default consumer based on environment name.
|
|
1993
|
+
// Without this, Nitro's configEnvironment creates a server-side
|
|
1994
|
+
// service for the client environment, causing virtual module
|
|
1995
|
+
// imports to leak to Node's native ESM loader (ERR_UNSUPPORTED_ESM_URL_SCHEME).
|
|
1996
|
+
consumer: "client",
|
|
1979
1997
|
optimizeDeps: {
|
|
1980
1998
|
exclude: ["vinext"],
|
|
1981
1999
|
// React packages aren't crawled from app/ source files,
|
|
@@ -2013,6 +2031,7 @@ hydrate();
|
|
|
2013
2031
|
// and there's no client-side hydration.
|
|
2014
2032
|
viteConfig.environments = {
|
|
2015
2033
|
client: {
|
|
2034
|
+
consumer: "client",
|
|
2016
2035
|
build: {
|
|
2017
2036
|
manifest: true,
|
|
2018
2037
|
ssrManifest: true,
|
|
@@ -2110,10 +2129,10 @@ hydrate();
|
|
|
2110
2129
|
}
|
|
2111
2130
|
// App Router virtual modules
|
|
2112
2131
|
if (id === RESOLVED_RSC_ENTRY && hasAppDir) {
|
|
2113
|
-
const routes = await appRouter(appDir);
|
|
2132
|
+
const routes = await appRouter(appDir, nextConfig?.pageExtensions, fileMatcher);
|
|
2114
2133
|
const metaRoutes = scanMetadataFiles(appDir);
|
|
2115
2134
|
// Check for global-error.tsx at app root
|
|
2116
|
-
const globalErrorPath = findFileWithExts(appDir, "global-error");
|
|
2135
|
+
const globalErrorPath = findFileWithExts(appDir, "global-error", fileMatcher);
|
|
2117
2136
|
return generateRscEntry(appDir, routes, middlewarePath, metaRoutes, globalErrorPath, nextConfig?.basePath, nextConfig?.trailingSlash, {
|
|
2118
2137
|
redirects: nextConfig?.redirects,
|
|
2119
2138
|
rewrites: nextConfig?.rewrites,
|
|
@@ -2207,24 +2226,22 @@ hydrate();
|
|
|
2207
2226
|
{
|
|
2208
2227
|
name: "vinext:pages-router",
|
|
2209
2228
|
// HMR: trigger full-reload for Pages Router page changes.
|
|
2210
|
-
//
|
|
2211
|
-
//
|
|
2212
|
-
//
|
|
2213
|
-
//
|
|
2214
|
-
//
|
|
2215
|
-
// ensures changes are always reflected in the browser.
|
|
2229
|
+
// Even with @vitejs/plugin-react providing React Fast Refresh,
|
|
2230
|
+
// the Pages Router injects hydration via inline <script type="module">
|
|
2231
|
+
// which may not be tracked in Vite's module graph. Explicitly
|
|
2232
|
+
// sending full-reload ensures changes are always reflected in
|
|
2233
|
+
// the browser.
|
|
2216
2234
|
hotUpdate(options) {
|
|
2217
2235
|
if (!hasPagesDir || hasAppDir)
|
|
2218
2236
|
return;
|
|
2219
|
-
|
|
2220
|
-
if (options.file.startsWith(pagesDir) && ext.test(options.file)) {
|
|
2237
|
+
if (options.file.startsWith(pagesDir) && fileMatcher.extensionRegex.test(options.file)) {
|
|
2221
2238
|
options.server.environments.client.hot.send({ type: "full-reload" });
|
|
2222
2239
|
return [];
|
|
2223
2240
|
}
|
|
2224
2241
|
},
|
|
2225
2242
|
configureServer(server) {
|
|
2226
2243
|
// Watch pages directory for file additions/removals to invalidate route cache.
|
|
2227
|
-
const pageExtensions =
|
|
2244
|
+
const pageExtensions = fileMatcher.extensionRegex;
|
|
2228
2245
|
/**
|
|
2229
2246
|
* Invalidate the virtual RSC entry module in Vite's module graph.
|
|
2230
2247
|
*
|
|
@@ -2262,12 +2279,6 @@ hydrate();
|
|
|
2262
2279
|
invalidateRscEntryModule();
|
|
2263
2280
|
}
|
|
2264
2281
|
});
|
|
2265
|
-
// Run instrumentation.ts register() if present (once at server startup)
|
|
2266
|
-
if (instrumentationPath) {
|
|
2267
|
-
runInstrumentation(server, instrumentationPath).catch((err) => {
|
|
2268
|
-
console.error("[vinext] Instrumentation error:", err);
|
|
2269
|
-
});
|
|
2270
|
-
}
|
|
2271
2282
|
// ── Dev request origin check ─────────────────────────────────────
|
|
2272
2283
|
// Registered directly (not in the returned function) so it runs
|
|
2273
2284
|
// BEFORE Vite's built-in middleware. This ensures all requests
|
|
@@ -2291,6 +2302,15 @@ hydrate();
|
|
|
2291
2302
|
});
|
|
2292
2303
|
// Return a function to register middleware AFTER Vite's built-in middleware
|
|
2293
2304
|
return () => {
|
|
2305
|
+
// Run instrumentation.ts register() if present (once at server startup).
|
|
2306
|
+
// Must be inside the returned function — ssrLoadModule() requires the
|
|
2307
|
+
// SSR environment's transport channel, which is not initialized until
|
|
2308
|
+
// after configureServer() returns. (See issue #167)
|
|
2309
|
+
if (instrumentationPath) {
|
|
2310
|
+
runInstrumentation(server, instrumentationPath).catch((err) => {
|
|
2311
|
+
console.error("[vinext] Instrumentation error:", err);
|
|
2312
|
+
});
|
|
2313
|
+
}
|
|
2294
2314
|
// App Router request logging in dev server
|
|
2295
2315
|
//
|
|
2296
2316
|
// For App Router, the RSC plugin handles requests internally.
|
|
@@ -2587,10 +2607,20 @@ hydrate();
|
|
|
2587
2607
|
return;
|
|
2588
2608
|
}
|
|
2589
2609
|
}
|
|
2590
|
-
// Apply middleware response headers
|
|
2610
|
+
// Apply middleware response headers. Unpack
|
|
2611
|
+
// x-middleware-request-* headers into req.headers so
|
|
2612
|
+
// config has/missing conditions and downstream handlers
|
|
2613
|
+
// see middleware-modified cookies and headers.
|
|
2591
2614
|
if (result.responseHeaders) {
|
|
2615
|
+
const mwReqPrefix = "x-middleware-request-";
|
|
2592
2616
|
for (const [key, value] of result.responseHeaders) {
|
|
2593
|
-
|
|
2617
|
+
if (key.startsWith(mwReqPrefix)) {
|
|
2618
|
+
const realName = key.slice(mwReqPrefix.length);
|
|
2619
|
+
req.headers[realName] = value;
|
|
2620
|
+
}
|
|
2621
|
+
else if (!key.startsWith("x-middleware-")) {
|
|
2622
|
+
res.appendHeader(key, value);
|
|
2623
|
+
}
|
|
2594
2624
|
}
|
|
2595
2625
|
}
|
|
2596
2626
|
// Apply middleware rewrite (URL and optional status code)
|
|
@@ -2639,7 +2669,7 @@ hydrate();
|
|
|
2639
2669
|
const resolvedPathname = resolvedUrl.split("?")[0];
|
|
2640
2670
|
if (resolvedPathname.startsWith("/api/") ||
|
|
2641
2671
|
resolvedPathname === "/api") {
|
|
2642
|
-
const apiRoutes = await apiRouter(pagesDir);
|
|
2672
|
+
const apiRoutes = await apiRouter(pagesDir, nextConfig?.pageExtensions, fileMatcher);
|
|
2643
2673
|
const handled = await handleApiRoute(server, req, res, resolvedUrl, apiRoutes);
|
|
2644
2674
|
if (handled)
|
|
2645
2675
|
return;
|
|
@@ -2651,7 +2681,7 @@ hydrate();
|
|
|
2651
2681
|
res.end("404 - API route not found");
|
|
2652
2682
|
return;
|
|
2653
2683
|
}
|
|
2654
|
-
const routes = await pagesRouter(pagesDir);
|
|
2684
|
+
const routes = await pagesRouter(pagesDir, nextConfig?.pageExtensions, fileMatcher);
|
|
2655
2685
|
// Apply afterFiles rewrites — these run after initial route matching
|
|
2656
2686
|
// If beforeFiles already rewrote the URL, afterFiles still run on the
|
|
2657
2687
|
// *resolved* pathname. Next.js applies these when route matching succeeds
|
|
@@ -2666,7 +2696,7 @@ hydrate();
|
|
|
2666
2696
|
await proxyExternalRewriteNode(req, res, resolvedUrl);
|
|
2667
2697
|
return;
|
|
2668
2698
|
}
|
|
2669
|
-
const handler = createSSRHandler(server, routes, pagesDir, nextConfig?.i18n);
|
|
2699
|
+
const handler = createSSRHandler(server, routes, pagesDir, nextConfig?.i18n, fileMatcher);
|
|
2670
2700
|
const mwStatus = req.__vinextRewriteStatus;
|
|
2671
2701
|
// Try rendering the resolved URL
|
|
2672
2702
|
const match = matchRoute(resolvedUrl.split("?")[0], routes);
|
|
@@ -2700,6 +2730,41 @@ hydrate();
|
|
|
2700
2730
|
};
|
|
2701
2731
|
},
|
|
2702
2732
|
},
|
|
2733
|
+
// Strip server-only data-fetching exports (getServerSideProps, getStaticProps,
|
|
2734
|
+
// getStaticPaths) from page modules in the client bundle. These functions
|
|
2735
|
+
// often import server-only modules (database drivers, fs, etc.) that would
|
|
2736
|
+
// break or bloat the client bundle. Next.js does this via an SWC transform
|
|
2737
|
+
// (next-ssg-transform); we use Vite's parseAst + MagicString.
|
|
2738
|
+
//
|
|
2739
|
+
// Only applies to client builds (not SSR) and only to files under the
|
|
2740
|
+
// pages/ directory.
|
|
2741
|
+
{
|
|
2742
|
+
name: "vinext:strip-server-exports",
|
|
2743
|
+
transform: {
|
|
2744
|
+
// Only match page source files, not node_modules
|
|
2745
|
+
filter: { id: /\.(tsx?|jsx?|mjs)$/ },
|
|
2746
|
+
handler(code, id) {
|
|
2747
|
+
const ssr = this.environment?.name !== "client";
|
|
2748
|
+
if (ssr)
|
|
2749
|
+
return null;
|
|
2750
|
+
if (!hasPagesDir)
|
|
2751
|
+
return null;
|
|
2752
|
+
// Only transform files under the pages/ directory
|
|
2753
|
+
if (!id.startsWith(pagesDir))
|
|
2754
|
+
return null;
|
|
2755
|
+
// Skip API routes, _app, _document, _error
|
|
2756
|
+
const relativePath = id.slice(pagesDir.length);
|
|
2757
|
+
if (relativePath.startsWith("/api/") || relativePath === "/api")
|
|
2758
|
+
return null;
|
|
2759
|
+
if (/\/_(?:app|document|error)\b/.test(relativePath))
|
|
2760
|
+
return null;
|
|
2761
|
+
const result = stripServerExports(code);
|
|
2762
|
+
if (!result)
|
|
2763
|
+
return null;
|
|
2764
|
+
return { code: result, map: null };
|
|
2765
|
+
},
|
|
2766
|
+
},
|
|
2767
|
+
},
|
|
2703
2768
|
// Local image import transform:
|
|
2704
2769
|
// When a source file imports a local image (e.g., `import hero from './hero.jpg'`),
|
|
2705
2770
|
// this plugin transforms the default import to a StaticImageData object with
|
|
@@ -3342,6 +3407,7 @@ hydrate();
|
|
|
3342
3407
|
" Cache-Control: public, max-age=31536000, immutable",
|
|
3343
3408
|
"",
|
|
3344
3409
|
].join("\n");
|
|
3410
|
+
fs.mkdirSync(clientDir, { recursive: true });
|
|
3345
3411
|
fs.writeFileSync(headersPath, headersContent);
|
|
3346
3412
|
}
|
|
3347
3413
|
},
|
|
@@ -3400,6 +3466,101 @@ function extractConstraint(str, re) {
|
|
|
3400
3466
|
* :param+ — matches one or more segments
|
|
3401
3467
|
* (regex) — inline regex patterns in the source
|
|
3402
3468
|
*/
|
|
3469
|
+
/**
|
|
3470
|
+
* Strip server-only data-fetching exports from a page module's source code.
|
|
3471
|
+
* Returns the transformed code, or null if no changes were made.
|
|
3472
|
+
*
|
|
3473
|
+
* Handles:
|
|
3474
|
+
* - export (async) function getServerSideProps(...) { ... }
|
|
3475
|
+
* - export const getStaticProps = async (...) => { ... }
|
|
3476
|
+
* - export const getServerSideProps = someHelper;
|
|
3477
|
+
*/
|
|
3478
|
+
/**
|
|
3479
|
+
* Skip past balanced brackets/parens/braces starting at `pos` (which should
|
|
3480
|
+
* point to the opening bracket). Returns the position AFTER the closing bracket.
|
|
3481
|
+
* Handles nested brackets, string literals, and comments.
|
|
3482
|
+
*/
|
|
3483
|
+
/**
|
|
3484
|
+
* Strip server-only data-fetching exports (getServerSideProps,
|
|
3485
|
+
* getStaticProps, getStaticPaths) from page modules for the client
|
|
3486
|
+
* bundle. Uses Vite's parseAst (Rollup/acorn) for correct handling
|
|
3487
|
+
* of all export patterns including function expressions, arrow
|
|
3488
|
+
* functions with TS return types, and re-exports.
|
|
3489
|
+
*
|
|
3490
|
+
* Modeled after Next.js's SWC `next-ssg-transform`.
|
|
3491
|
+
*/
|
|
3492
|
+
function stripServerExports(code) {
|
|
3493
|
+
const SERVER_EXPORTS = new Set(["getServerSideProps", "getStaticProps", "getStaticPaths"]);
|
|
3494
|
+
if (![...SERVER_EXPORTS].some(name => code.includes(name)))
|
|
3495
|
+
return null;
|
|
3496
|
+
let ast;
|
|
3497
|
+
try {
|
|
3498
|
+
ast = parseAst(code);
|
|
3499
|
+
}
|
|
3500
|
+
catch {
|
|
3501
|
+
// If parsing fails (shouldn't happen post-JSX/TS transform), bail out
|
|
3502
|
+
return null;
|
|
3503
|
+
}
|
|
3504
|
+
const s = new MagicString(code);
|
|
3505
|
+
let changed = false;
|
|
3506
|
+
for (const node of ast.body) {
|
|
3507
|
+
if (node.type !== "ExportNamedDeclaration")
|
|
3508
|
+
continue;
|
|
3509
|
+
// Case 1: export function name() {} / export async function name() {}
|
|
3510
|
+
// Case 2: export const/let/var name = ...
|
|
3511
|
+
if (node.declaration) {
|
|
3512
|
+
const decl = node.declaration;
|
|
3513
|
+
if (decl.type === "FunctionDeclaration" && SERVER_EXPORTS.has(decl.id?.name)) {
|
|
3514
|
+
s.overwrite(node.start, node.end, `export function ${decl.id.name}() { return { props: {} }; }`);
|
|
3515
|
+
changed = true;
|
|
3516
|
+
}
|
|
3517
|
+
else if (decl.type === "VariableDeclaration") {
|
|
3518
|
+
for (const declarator of decl.declarations) {
|
|
3519
|
+
if (declarator.id?.type === "Identifier" && SERVER_EXPORTS.has(declarator.id.name)) {
|
|
3520
|
+
s.overwrite(node.start, node.end, `export const ${declarator.id.name} = undefined;`);
|
|
3521
|
+
changed = true;
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3525
|
+
continue;
|
|
3526
|
+
}
|
|
3527
|
+
// Case 3: export { getServerSideProps } or export { getServerSideProps as gSSP }
|
|
3528
|
+
if (node.specifiers && node.specifiers.length > 0 && !node.source) {
|
|
3529
|
+
const kept = [];
|
|
3530
|
+
const stripped = [];
|
|
3531
|
+
for (const spec of node.specifiers) {
|
|
3532
|
+
// spec.local.name is the binding name, spec.exported.name is the export name
|
|
3533
|
+
const exportedName = spec.exported?.name ?? spec.exported?.value;
|
|
3534
|
+
if (SERVER_EXPORTS.has(exportedName)) {
|
|
3535
|
+
stripped.push(exportedName);
|
|
3536
|
+
}
|
|
3537
|
+
else {
|
|
3538
|
+
kept.push(spec);
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3541
|
+
if (stripped.length > 0) {
|
|
3542
|
+
// Build replacement: keep non-server specifiers, add stubs for stripped ones
|
|
3543
|
+
const parts = [];
|
|
3544
|
+
if (kept.length > 0) {
|
|
3545
|
+
const keptStr = kept.map((sp) => {
|
|
3546
|
+
const local = sp.local.name;
|
|
3547
|
+
const exported = sp.exported?.name ?? sp.exported?.value;
|
|
3548
|
+
return local === exported ? local : `${local} as ${exported}`;
|
|
3549
|
+
}).join(", ");
|
|
3550
|
+
parts.push(`export { ${keptStr} };`);
|
|
3551
|
+
}
|
|
3552
|
+
for (const name of stripped) {
|
|
3553
|
+
parts.push(`export const ${name} = undefined;`);
|
|
3554
|
+
}
|
|
3555
|
+
s.overwrite(node.start, node.end, parts.join("\n"));
|
|
3556
|
+
changed = true;
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3559
|
+
}
|
|
3560
|
+
if (!changed)
|
|
3561
|
+
return null;
|
|
3562
|
+
return s.toString();
|
|
3563
|
+
}
|
|
3403
3564
|
export function matchConfigPattern(pathname, pattern) {
|
|
3404
3565
|
// If the pattern contains regex groups like (\\d+) or (.*), use regex matching.
|
|
3405
3566
|
// Also enter this branch when a catch-all parameter (:param* or :param+) is
|
|
@@ -3610,16 +3771,44 @@ function applyRewrites(pathname, rewrites, ctx) {
|
|
|
3610
3771
|
function applyHeaders(pathname, res, headers, ctx) {
|
|
3611
3772
|
const matched = matchHeaders(pathname, headers, ctx);
|
|
3612
3773
|
for (const header of matched) {
|
|
3613
|
-
|
|
3774
|
+
// Use append semantics for headers where multiple values must coexist
|
|
3775
|
+
// (Vary, Set-Cookie). Using setHeader() on these would destroy
|
|
3776
|
+
// existing values like "Vary: RSC, Accept".
|
|
3777
|
+
const lk = header.key.toLowerCase();
|
|
3778
|
+
if (lk === "set-cookie") {
|
|
3779
|
+
// Node.js res.getHeader("set-cookie") returns string[] when
|
|
3780
|
+
// multiple Set-Cookie headers have been set. Preserve the array.
|
|
3781
|
+
const existing = res.getHeader(lk);
|
|
3782
|
+
if (Array.isArray(existing)) {
|
|
3783
|
+
res.setHeader(header.key, [...existing, header.value]);
|
|
3784
|
+
}
|
|
3785
|
+
else if (existing) {
|
|
3786
|
+
res.setHeader(header.key, [String(existing), header.value]);
|
|
3787
|
+
}
|
|
3788
|
+
else {
|
|
3789
|
+
res.setHeader(header.key, header.value);
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
else if (lk === "vary") {
|
|
3793
|
+
const existing = res.getHeader(lk);
|
|
3794
|
+
if (existing) {
|
|
3795
|
+
res.setHeader(header.key, existing + ", " + header.value);
|
|
3796
|
+
}
|
|
3797
|
+
else {
|
|
3798
|
+
res.setHeader(header.key, header.value);
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3801
|
+
else {
|
|
3802
|
+
res.setHeader(header.key, header.value);
|
|
3803
|
+
}
|
|
3614
3804
|
}
|
|
3615
3805
|
}
|
|
3616
3806
|
/**
|
|
3617
3807
|
* Find a file by name (without extension) in a directory.
|
|
3618
|
-
* Checks
|
|
3808
|
+
* Checks the configured page extensions.
|
|
3619
3809
|
*/
|
|
3620
|
-
function findFileWithExts(dir, name) {
|
|
3621
|
-
const
|
|
3622
|
-
for (const ext of extensions) {
|
|
3810
|
+
function findFileWithExts(dir, name, matcher) {
|
|
3811
|
+
for (const ext of matcher.dottedExtensions) {
|
|
3623
3812
|
const filePath = path.join(dir, name + ext);
|
|
3624
3813
|
if (fs.existsSync(filePath))
|
|
3625
3814
|
return filePath;
|
|
@@ -3664,4 +3853,5 @@ export { staticExportPages, staticExportApp } from "./build/static-export.js";
|
|
|
3664
3853
|
export { clientManualChunks, clientOutputConfig, clientTreeshakeConfig, computeLazyChunks };
|
|
3665
3854
|
export { resolvePostcssStringPlugins as _resolvePostcssStringPlugins };
|
|
3666
3855
|
export { parseStaticObjectLiteral as _parseStaticObjectLiteral };
|
|
3856
|
+
export { stripServerExports as _stripServerExports };
|
|
3667
3857
|
//# sourceMappingURL=index.js.map
|