vinext 0.0.15 → 0.0.17

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.
Files changed (50) hide show
  1. package/README.md +6 -0
  2. package/dist/cli.js +21 -11
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config/config-matchers.d.ts +13 -7
  5. package/dist/config/config-matchers.d.ts.map +1 -1
  6. package/dist/config/config-matchers.js +18 -9
  7. package/dist/config/config-matchers.js.map +1 -1
  8. package/dist/config/next-config.d.ts +2 -0
  9. package/dist/config/next-config.d.ts.map +1 -1
  10. package/dist/config/next-config.js.map +1 -1
  11. package/dist/deploy.d.ts.map +1 -1
  12. package/dist/deploy.js +3 -1
  13. package/dist/deploy.js.map +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +75 -62
  16. package/dist/index.js.map +1 -1
  17. package/dist/routing/app-router.d.ts.map +1 -1
  18. package/dist/routing/app-router.js +3 -2
  19. package/dist/routing/app-router.js.map +1 -1
  20. package/dist/routing/pages-router.d.ts.map +1 -1
  21. package/dist/routing/pages-router.js +2 -1
  22. package/dist/routing/pages-router.js.map +1 -1
  23. package/dist/server/app-dev-server.d.ts.map +1 -1
  24. package/dist/server/app-dev-server.js +43 -9
  25. package/dist/server/app-dev-server.js.map +1 -1
  26. package/dist/server/middleware.d.ts +23 -1
  27. package/dist/server/middleware.d.ts.map +1 -1
  28. package/dist/server/middleware.js +42 -8
  29. package/dist/server/middleware.js.map +1 -1
  30. package/dist/server/prod-server.js +1 -1
  31. package/dist/server/prod-server.js.map +1 -1
  32. package/dist/shims/fetch-cache.d.ts.map +1 -1
  33. package/dist/shims/fetch-cache.js +73 -41
  34. package/dist/shims/fetch-cache.js.map +1 -1
  35. package/dist/shims/font-google-base.d.ts +68 -0
  36. package/dist/shims/font-google-base.d.ts.map +1 -0
  37. package/dist/shims/font-google-base.js +365 -0
  38. package/dist/shims/font-google-base.js.map +1 -0
  39. package/dist/shims/font-google.d.ts +2 -121
  40. package/dist/shims/font-google.d.ts.map +1 -1
  41. package/dist/shims/font-google.generated.d.ts +1925 -0
  42. package/dist/shims/font-google.generated.d.ts.map +1 -0
  43. package/dist/shims/font-google.generated.js +1928 -0
  44. package/dist/shims/font-google.generated.js.map +1 -0
  45. package/dist/shims/font-google.js +2 -386
  46. package/dist/shims/font-google.js.map +1 -1
  47. package/dist/utils/project.d.ts.map +1 -1
  48. package/dist/utils/project.js +4 -0
  49. package/dist/utils/project.js.map +1 -1
  50. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -5,12 +5,12 @@ import { createSSRHandler } from "./server/dev-server.js";
5
5
  import { handleApiRoute } from "./server/api-handler.js";
6
6
  import { generateRscEntry, generateSsrEntry, generateBrowserEntry, } from "./server/app-dev-server.js";
7
7
  import { loadNextConfig, resolveNextConfig, } from "./config/next-config.js";
8
- import { findMiddlewareFile, runMiddleware } from "./server/middleware.js";
8
+ import { findMiddlewareFile, isProxyFile, runMiddleware } from "./server/middleware.js";
9
9
  import { generateSafeRegExpCode, generateMiddlewareMatcherCode, generateNormalizePathCode } from "./server/middleware-codegen.js";
10
10
  import { normalizePath } from "./server/normalize-path.js";
11
11
  import { findInstrumentationFile, runInstrumentation } from "./server/instrumentation.js";
12
12
  import { validateDevRequest } from "./server/dev-origin-check.js";
13
- import { safeRegExp, escapeHeaderSource, isExternalUrl, proxyExternalRequest } from "./config/config-matchers.js";
13
+ import { safeRegExp, isExternalUrl, proxyExternalRequest, parseCookies, matchHeaders, matchRedirect, matchRewrite, } from "./config/config-matchers.js";
14
14
  import { scanMetadataFiles } from "./server/metadata-routes.js";
15
15
  import tsconfigPaths from "vite-tsconfig-paths";
16
16
  import MagicString from "magic-string";
@@ -18,6 +18,7 @@ import path from "node:path";
18
18
  import { fileURLToPath, pathToFileURL } from "node:url";
19
19
  import { createRequire } from "node:module";
20
20
  import fs from "node:fs";
21
+ import commonjs from "vite-plugin-commonjs";
21
22
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
22
23
  /**
23
24
  * Fetch Google Fonts CSS, download .woff2 files, cache locally, and return
@@ -493,6 +494,7 @@ export default function vinext(options = {}) {
493
494
  let middlewarePath = null;
494
495
  let instrumentationPath = null;
495
496
  let hasCloudflarePlugin = false;
497
+ let hasNitroPlugin = false;
496
498
  // Resolve shim paths - works both from source (.ts) and built (.js)
497
499
  const shimsDir = path.resolve(__dirname, "shims");
498
500
  // Shim alias map — populated in config(), used by resolveId() for .js variants
@@ -568,8 +570,15 @@ ${generateSafeRegExpCode("es5")}
568
570
  ${generateMiddlewareMatcherCode("es5")}
569
571
 
570
572
  export async function runMiddleware(request) {
571
- var middlewareFn = middlewareModule.default || middlewareModule.middleware;
572
- if (typeof middlewareFn !== "function") return { continue: true };
573
+ var isProxy = ${middlewarePath ? JSON.stringify(isProxyFile(middlewarePath)) : "false"};
574
+ var middlewareFn = isProxy
575
+ ? (middlewareModule.proxy ?? middlewareModule.default)
576
+ : (middlewareModule.middleware ?? middlewareModule.default);
577
+ if (typeof middlewareFn !== "function") {
578
+ var fileType = isProxy ? "Proxy" : "Middleware";
579
+ var expectedExport = isProxy ? "proxy" : "middleware";
580
+ throw new Error("The " + fileType + " file must export a function named \`" + expectedExport + "\` or a \`default\` function.");
581
+ }
573
582
 
574
583
  var config = middlewareModule.config;
575
584
  var matcher = config && config.matcher;
@@ -606,9 +615,13 @@ export async function runMiddleware(request) {
606
615
  if (response.headers.get("x-middleware-next") === "1") {
607
616
  var rHeaders = new Headers();
608
617
  for (var [key, value] of response.headers) {
609
- // Strip ALL x-middleware-* headers they are internal routing signals
610
- // and must never reach clients.
611
- if (!key.startsWith("x-middleware-")) rHeaders.append(key, value);
618
+ // Keep x-middleware-request-* headers so the production server can
619
+ // apply middleware-request header overrides before stripping internals
620
+ // from the final client response.
621
+ if (
622
+ !key.startsWith("x-middleware-") ||
623
+ key.startsWith("x-middleware-request-")
624
+ ) rHeaders.append(key, value);
612
625
  }
613
626
  return { continue: true, responseHeaders: rHeaders };
614
627
  }
@@ -621,7 +634,9 @@ export async function runMiddleware(request) {
621
634
  var rewriteUrl = response.headers.get("x-middleware-rewrite");
622
635
  if (rewriteUrl) {
623
636
  var rwHeaders = new Headers();
624
- for (var [k, v] of response.headers) { if (!k.startsWith("x-middleware-")) rwHeaders.append(k, v); }
637
+ for (var [k, v] of response.headers) {
638
+ if (!k.startsWith("x-middleware-") || k.startsWith("x-middleware-request-")) rwHeaders.append(k, v);
639
+ }
625
640
  var rewritePath;
626
641
  try { var parsed = new URL(rewriteUrl, request.url); rewritePath = parsed.pathname + parsed.search; }
627
642
  catch { rewritePath = rewriteUrl; }
@@ -1534,6 +1549,9 @@ hydrate();
1534
1549
  // Resolve tsconfig paths/baseUrl aliases so real-world Next.js repos
1535
1550
  // that use @/*, #/*, or baseUrl imports work out of the box.
1536
1551
  tsconfigPaths(),
1552
+ // Transform CJS require()/module.exports to ESM before other plugins
1553
+ // analyze imports (RSC directive scanning, shim resolution, etc.)
1554
+ commonjs(),
1537
1555
  {
1538
1556
  name: "vinext:config",
1539
1557
  enforce: "pre",
@@ -1664,6 +1682,7 @@ hydrate();
1664
1682
  }
1665
1683
  flattenPlugins(config.plugins ?? []);
1666
1684
  hasCloudflarePlugin = pluginsFlat.some((p) => p && typeof p === "object" && typeof p.name === "string" && (p.name === "vite-plugin-cloudflare" || p.name.startsWith("vite-plugin-cloudflare:")));
1685
+ hasNitroPlugin = pluginsFlat.some((p) => p && typeof p === "object" && typeof p.name === "string" && (p.name === "nitro" || p.name.startsWith("nitro:")));
1667
1686
  // Resolve PostCSS string plugin names that Vite can't handle.
1668
1687
  // Next.js projects commonly use array-form plugins like
1669
1688
  // `plugins: ["@tailwindcss/postcss"]` which postcss-load-config
@@ -1715,7 +1734,7 @@ hydrate();
1715
1734
  // In multi-env builds, manualChunks must only be set per-environment
1716
1735
  // (on the client env), not globally — otherwise it leaks into RSC/SSR
1717
1736
  // environments where it can cause asset resolution issues.
1718
- const isMultiEnv = hasAppDir || hasCloudflarePlugin;
1737
+ const isMultiEnv = hasAppDir || hasCloudflarePlugin || hasNitroPlugin;
1719
1738
  const viteConfig = {
1720
1739
  // Disable Vite's default HTML serving - we handle all routing
1721
1740
  appType: "custom",
@@ -1777,8 +1796,8 @@ hydrate();
1777
1796
  },
1778
1797
  // Externalize React packages from SSR transform — they are CJS and
1779
1798
  // must be loaded natively by Node, not through Vite's ESM evaluator.
1780
- // Skip when targeting Cloudflare Workers (they bundle everything).
1781
- ...(hasCloudflarePlugin ? {} : {
1799
+ // Skip when targeting bundled runtimes (Cloudflare/Nitro bundle everything).
1800
+ ...(hasCloudflarePlugin || hasNitroPlugin ? {} : {
1782
1801
  ssr: {
1783
1802
  external: ["react", "react-dom", "react-dom/server"],
1784
1803
  },
@@ -1829,14 +1848,14 @@ hydrate();
1829
1848
  ];
1830
1849
  viteConfig.environments = {
1831
1850
  rsc: {
1832
- ...(hasCloudflarePlugin ? {} : {
1851
+ ...(hasCloudflarePlugin || hasNitroPlugin ? {} : {
1833
1852
  resolve: {
1834
1853
  // Externalize native/heavy packages so the RSC environment
1835
1854
  // loads them natively via Node rather than through Vite's
1836
1855
  // ESM module evaluator (which can't handle native addons).
1837
1856
  // Note: Do NOT externalize react/react-dom here — they must
1838
1857
  // be bundled with the "react-server" condition for RSC.
1839
- // Skip when targeting Cloudflare Workers.
1858
+ // Skip when targeting bundled runtimes (Cloudflare/Nitro).
1840
1859
  external: [
1841
1860
  "satori",
1842
1861
  "@resvg/resvg-js",
@@ -1870,11 +1889,15 @@ hydrate();
1870
1889
  client: {
1871
1890
  optimizeDeps: {
1872
1891
  exclude: ["vinext"],
1873
- // react and react-dom are framework dependencies used for
1874
- // hydration. They aren't crawled from app/ source files so
1875
- // must be pre-included to prevent late discovery and page
1876
- // reloads during development.
1877
- include: ["react", "react-dom", "react-dom/client"],
1892
+ // React packages aren't crawled from app/ source files,
1893
+ // so must be pre-included to avoid late discovery (#25).
1894
+ include: [
1895
+ "react",
1896
+ "react-dom",
1897
+ "react-dom/client",
1898
+ "react/jsx-runtime",
1899
+ "react/jsx-dev-runtime",
1900
+ ],
1878
1901
  },
1879
1902
  build: {
1880
1903
  // When targeting Cloudflare Workers, enable manifest generation
@@ -2297,13 +2320,25 @@ hydrate();
2297
2320
  req.__vinextRewriteStatus = result.rewriteStatus;
2298
2321
  }
2299
2322
  }
2323
+ // Build request context once for has/missing condition checks
2324
+ // across headers, redirects, and rewrites.
2325
+ const reqUrl = new URL(url, `http://${req.headers.host || "localhost"}`);
2326
+ const reqCtxHeaders = new Headers(Object.fromEntries(Object.entries(req.headers)
2327
+ .filter(([, v]) => v !== undefined)
2328
+ .map(([k, v]) => [k, Array.isArray(v) ? v.join(", ") : String(v)])));
2329
+ const reqCtx = {
2330
+ headers: reqCtxHeaders,
2331
+ cookies: parseCookies(reqCtxHeaders.get("cookie")),
2332
+ query: reqUrl.searchParams,
2333
+ host: reqCtxHeaders.get("host") ?? reqUrl.host,
2334
+ };
2300
2335
  // Apply custom headers from next.config.js
2301
2336
  if (nextConfig?.headers.length) {
2302
- applyHeaders(pathname, res, nextConfig.headers);
2337
+ applyHeaders(pathname, res, nextConfig.headers, reqCtx);
2303
2338
  }
2304
2339
  // Apply redirects from next.config.js
2305
2340
  if (nextConfig?.redirects.length) {
2306
- const redirected = applyRedirects(pathname, res, nextConfig.redirects);
2341
+ const redirected = applyRedirects(pathname, res, nextConfig.redirects, reqCtx);
2307
2342
  if (redirected)
2308
2343
  return;
2309
2344
  }
@@ -2311,7 +2346,7 @@ hydrate();
2311
2346
  let resolvedUrl = url;
2312
2347
  if (nextConfig?.rewrites.beforeFiles.length) {
2313
2348
  resolvedUrl =
2314
- applyRewrites(pathname, nextConfig.rewrites.beforeFiles) ??
2349
+ applyRewrites(pathname, nextConfig.rewrites.beforeFiles, reqCtx) ??
2315
2350
  url;
2316
2351
  }
2317
2352
  // External rewrite from beforeFiles — proxy to external URL
@@ -2338,7 +2373,7 @@ hydrate();
2338
2373
  // *resolved* pathname. Next.js applies these when route matching succeeds
2339
2374
  // but allows overriding with rewrites.
2340
2375
  if (nextConfig?.rewrites.afterFiles.length) {
2341
- const afterRewrite = applyRewrites(resolvedUrl.split("?")[0], nextConfig.rewrites.afterFiles);
2376
+ const afterRewrite = applyRewrites(resolvedUrl.split("?")[0], nextConfig.rewrites.afterFiles, reqCtx);
2342
2377
  if (afterRewrite)
2343
2378
  resolvedUrl = afterRewrite;
2344
2379
  }
@@ -2357,7 +2392,7 @@ hydrate();
2357
2392
  }
2358
2393
  // No route matched — try fallback rewrites
2359
2394
  if (nextConfig?.rewrites.fallback.length) {
2360
- const fallbackRewrite = applyRewrites(resolvedUrl.split("?")[0], nextConfig.rewrites.fallback);
2395
+ const fallbackRewrite = applyRewrites(resolvedUrl.split("?")[0], nextConfig.rewrites.fallback, reqCtx);
2361
2396
  if (fallbackRewrite) {
2362
2397
  // External fallback rewrite — proxy to external URL
2363
2398
  if (isExternalUrl(fallbackRewrite)) {
@@ -3172,22 +3207,14 @@ function sanitizeDestinationLocal(dest) {
3172
3207
  * Apply redirect rules from next.config.js.
3173
3208
  * Returns true if a redirect was applied.
3174
3209
  */
3175
- function applyRedirects(pathname, res, redirects) {
3176
- for (const redirect of redirects) {
3177
- const params = matchConfigPattern(pathname, redirect.source);
3178
- if (params) {
3179
- let dest = redirect.destination;
3180
- for (const [key, value] of Object.entries(params)) {
3181
- dest = dest.replace(`:${key}*`, value);
3182
- dest = dest.replace(`:${key}+`, value);
3183
- dest = dest.replace(`:${key}`, value);
3184
- }
3185
- // Sanitize to prevent open redirect via protocol-relative URLs
3186
- dest = sanitizeDestinationLocal(dest);
3187
- res.writeHead(redirect.permanent ? 308 : 307, { Location: dest });
3188
- res.end();
3189
- return true;
3190
- }
3210
+ function applyRedirects(pathname, res, redirects, ctx) {
3211
+ const result = matchRedirect(pathname, redirects, ctx);
3212
+ if (result) {
3213
+ // Sanitize to prevent open redirect via protocol-relative URLs
3214
+ const dest = sanitizeDestinationLocal(result.destination);
3215
+ res.writeHead(result.permanent ? 308 : 307, { Location: dest });
3216
+ res.end();
3217
+ return true;
3191
3218
  }
3192
3219
  return false;
3193
3220
  }
@@ -3254,35 +3281,21 @@ async function proxyExternalRewriteNode(req, res, externalUrl) {
3254
3281
  * Apply rewrite rules from next.config.js.
3255
3282
  * Returns the rewritten URL or null if no rewrite matched.
3256
3283
  */
3257
- function applyRewrites(pathname, rewrites) {
3258
- for (const rewrite of rewrites) {
3259
- const params = matchConfigPattern(pathname, rewrite.source);
3260
- if (params) {
3261
- let dest = rewrite.destination;
3262
- for (const [key, value] of Object.entries(params)) {
3263
- dest = dest.replace(`:${key}*`, value);
3264
- dest = dest.replace(`:${key}+`, value);
3265
- dest = dest.replace(`:${key}`, value);
3266
- }
3267
- // Sanitize to prevent open redirect via protocol-relative URLs
3268
- dest = sanitizeDestinationLocal(dest);
3269
- return dest;
3270
- }
3284
+ function applyRewrites(pathname, rewrites, ctx) {
3285
+ const dest = matchRewrite(pathname, rewrites, ctx);
3286
+ if (dest) {
3287
+ // Sanitize to prevent open redirect via protocol-relative URLs
3288
+ return sanitizeDestinationLocal(dest);
3271
3289
  }
3272
3290
  return null;
3273
3291
  }
3274
3292
  /**
3275
3293
  * Apply custom header rules from next.config.js.
3276
3294
  */
3277
- function applyHeaders(pathname, res, headers) {
3278
- for (const rule of headers) {
3279
- const escaped = escapeHeaderSource(rule.source);
3280
- const sourceRegex = safeRegExp("^" + escaped + "$");
3281
- if (sourceRegex && sourceRegex.test(pathname)) {
3282
- for (const header of rule.headers) {
3283
- res.setHeader(header.key, header.value);
3284
- }
3285
- }
3295
+ function applyHeaders(pathname, res, headers, ctx) {
3296
+ const matched = matchHeaders(pathname, headers, ctx);
3297
+ for (const header of matched) {
3298
+ res.setHeader(header.key, header.value);
3286
3299
  }
3287
3300
  }
3288
3301
  /**