vinext 0.0.23 → 0.0.25

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 (105) hide show
  1. package/README.md +24 -0
  2. package/dist/check.d.ts.map +1 -1
  3. package/dist/check.js +3 -2
  4. package/dist/check.js.map +1 -1
  5. package/dist/client/entry.js +1 -1
  6. package/dist/client/entry.js.map +1 -1
  7. package/dist/config/config-matchers.d.ts +21 -0
  8. package/dist/config/config-matchers.d.ts.map +1 -1
  9. package/dist/config/config-matchers.js +59 -9
  10. package/dist/config/config-matchers.js.map +1 -1
  11. package/dist/config/next-config.d.ts +8 -2
  12. package/dist/config/next-config.d.ts.map +1 -1
  13. package/dist/config/next-config.js +90 -35
  14. package/dist/config/next-config.js.map +1 -1
  15. package/dist/deploy.d.ts +10 -0
  16. package/dist/deploy.d.ts.map +1 -1
  17. package/dist/deploy.js +140 -56
  18. package/dist/deploy.js.map +1 -1
  19. package/dist/index.d.ts +14 -2
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +195 -25
  22. package/dist/index.js.map +1 -1
  23. package/dist/plugins/async-hooks-stub.d.ts +16 -0
  24. package/dist/plugins/async-hooks-stub.d.ts.map +1 -0
  25. package/dist/plugins/async-hooks-stub.js +45 -0
  26. package/dist/plugins/async-hooks-stub.js.map +1 -0
  27. package/dist/routing/app-router.d.ts +12 -6
  28. package/dist/routing/app-router.d.ts.map +1 -1
  29. package/dist/routing/app-router.js +19 -40
  30. package/dist/routing/app-router.js.map +1 -1
  31. package/dist/routing/pages-router.d.ts.map +1 -1
  32. package/dist/routing/pages-router.js +3 -9
  33. package/dist/routing/pages-router.js.map +1 -1
  34. package/dist/routing/utils.d.ts +9 -0
  35. package/dist/routing/utils.d.ts.map +1 -1
  36. package/dist/routing/utils.js +10 -0
  37. package/dist/routing/utils.js.map +1 -1
  38. package/dist/server/api-handler.d.ts.map +1 -1
  39. package/dist/server/api-handler.js +6 -0
  40. package/dist/server/api-handler.js.map +1 -1
  41. package/dist/server/app-dev-server.d.ts +2 -2
  42. package/dist/server/app-dev-server.d.ts.map +1 -1
  43. package/dist/server/app-dev-server.js +294 -133
  44. package/dist/server/app-dev-server.js.map +1 -1
  45. package/dist/server/dev-module-runner.d.ts +84 -0
  46. package/dist/server/dev-module-runner.d.ts.map +1 -0
  47. package/dist/server/dev-module-runner.js +105 -0
  48. package/dist/server/dev-module-runner.js.map +1 -0
  49. package/dist/server/dev-server.js.map +1 -1
  50. package/dist/server/instrumentation.d.ts +52 -9
  51. package/dist/server/instrumentation.d.ts.map +1 -1
  52. package/dist/server/instrumentation.js +52 -15
  53. package/dist/server/instrumentation.js.map +1 -1
  54. package/dist/server/middleware.d.ts +7 -3
  55. package/dist/server/middleware.d.ts.map +1 -1
  56. package/dist/server/middleware.js +16 -6
  57. package/dist/server/middleware.js.map +1 -1
  58. package/dist/server/prod-server.d.ts +8 -2
  59. package/dist/server/prod-server.d.ts.map +1 -1
  60. package/dist/server/prod-server.js +56 -35
  61. package/dist/server/prod-server.js.map +1 -1
  62. package/dist/server/worker-utils.d.ts +15 -0
  63. package/dist/server/worker-utils.d.ts.map +1 -0
  64. package/dist/server/worker-utils.js +41 -0
  65. package/dist/server/worker-utils.js.map +1 -0
  66. package/dist/shims/cache.d.ts.map +1 -1
  67. package/dist/shims/cache.js +14 -2
  68. package/dist/shims/cache.js.map +1 -1
  69. package/dist/shims/fetch-cache.d.ts.map +1 -1
  70. package/dist/shims/fetch-cache.js +139 -29
  71. package/dist/shims/fetch-cache.js.map +1 -1
  72. package/dist/shims/form.d.ts.map +1 -1
  73. package/dist/shims/form.js +2 -3
  74. package/dist/shims/form.js.map +1 -1
  75. package/dist/shims/headers.d.ts +6 -0
  76. package/dist/shims/headers.d.ts.map +1 -1
  77. package/dist/shims/headers.js +8 -0
  78. package/dist/shims/headers.js.map +1 -1
  79. package/dist/shims/layout-segment-context.d.ts +5 -4
  80. package/dist/shims/layout-segment-context.d.ts.map +1 -1
  81. package/dist/shims/layout-segment-context.js +6 -5
  82. package/dist/shims/layout-segment-context.js.map +1 -1
  83. package/dist/shims/link.d.ts.map +1 -1
  84. package/dist/shims/link.js +32 -17
  85. package/dist/shims/link.js.map +1 -1
  86. package/dist/shims/navigation.d.ts +14 -11
  87. package/dist/shims/navigation.d.ts.map +1 -1
  88. package/dist/shims/navigation.js +122 -102
  89. package/dist/shims/navigation.js.map +1 -1
  90. package/dist/shims/router.d.ts.map +1 -1
  91. package/dist/shims/router.js +37 -21
  92. package/dist/shims/router.js.map +1 -1
  93. package/dist/shims/server.d.ts +2 -0
  94. package/dist/shims/server.d.ts.map +1 -1
  95. package/dist/shims/server.js +4 -0
  96. package/dist/shims/server.js.map +1 -1
  97. package/dist/shims/url-utils.d.ts +13 -0
  98. package/dist/shims/url-utils.d.ts.map +1 -0
  99. package/dist/shims/url-utils.js +28 -0
  100. package/dist/shims/url-utils.js.map +1 -0
  101. package/dist/utils/project.d.ts +13 -1
  102. package/dist/utils/project.d.ts.map +1 -1
  103. package/dist/utils/project.js +63 -13
  104. package/dist/utils/project.js.map +1 -1
  105. package/package.json +6 -1
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../src/deploy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAQH,OAAO,EACL,cAAc,IAAI,eAAe,EACjC,gBAAgB,IAAI,iBAAiB,EAEtC,MAAM,oBAAoB,CAAC;AAO5B,MAAM,WAAW,aAAa;IAC5B,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gEAAgE;IAChE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,4DAA4D;IAC5D,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAkBD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE;;;;;;;;;;;EAc7C;AAID,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,cAAc,EAAE,OAAO,CAAC;IACxB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,MAAM,EAAE,OAAO,CAAC;IAChB,wCAAwC;IACxC,aAAa,EAAE,OAAO,CAAC;IACvB,4CAA4C;IAC5C,MAAM,EAAE,OAAO,CAAC;IAChB,+BAA+B;IAC/B,WAAW,EAAE,OAAO,CAAC;IACrB,yDAAyD;IACzD,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B;AAID,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CA0GvD;AAwHD,mCAAmC;AACnC,eAAO,MAAM,cAAc,wBAAkB,CAAC;AAE9C,qCAAqC;AACrC,eAAO,MAAM,gBAAgB,0BAAoB,CAAC;AAIlD,sCAAsC;AACtC,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAiChE;AAED,8CAA8C;AAC9C,wBAAgB,4BAA4B,IAAI,MAAM,CAoDrD;AAED,gDAAgD;AAChD,wBAAgB,8BAA8B,IAAI,MAAM,CAiSvD;AAED,6CAA6C;AAC7C,wBAAgB,2BAA2B,CAAC,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAgDtE;AAED,+CAA+C;AAC/C,wBAAgB,6BAA6B,CAAC,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAmCxE;AAID,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAQ9E;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,WAAW;AACjB,+DAA+D;AAC/D,aAAa,GAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAA6B,GAC1E,UAAU,EAAE,CA6Bd;AAoBD,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG,aAAa,EAAE,CAkCrE;AAmCD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;CACzB;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,GAAG,KAAK,CAAC,GAAG,kBAAkB,CAO3G;AAwCD,wBAAsB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAoGlE"}
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../src/deploy.ts"],"names":[],"mappings":"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"}
package/dist/deploy.js CHANGED
@@ -19,7 +19,7 @@ import { createRequire } from "node:module";
19
19
  import { execFileSync } from "node:child_process";
20
20
  import { parseArgs as nodeParseArgs } from "node:util";
21
21
  import { createBuilder, build } from "vite";
22
- import { ensureESModule as _ensureESModule, renameCJSConfigs as _renameCJSConfigs, detectPackageManager as _detectPackageManager, } from "./utils/project.js";
22
+ import { ensureESModule as _ensureESModule, renameCJSConfigs as _renameCJSConfigs, detectPackageManager as _detectPackageManager, findInNodeModules as _findInNodeModules, } from "./utils/project.js";
23
23
  import { getReactUpgradeDeps } from "./init.js";
24
24
  import { runTPR } from "./cloudflare/tpr.js";
25
25
  import { loadDotenv } from "./config/dotenv.js";
@@ -69,10 +69,12 @@ export function detectProject(root) {
69
69
  fs.existsSync(path.join(root, "wrangler.toml"));
70
70
  const hasWorkerEntry = fs.existsSync(path.join(root, "worker", "index.ts")) ||
71
71
  fs.existsSync(path.join(root, "worker", "index.js"));
72
- // Check node_modules for installed packages
73
- const hasCloudflarePlugin = fs.existsSync(path.join(root, "node_modules", "@cloudflare", "vite-plugin"));
74
- const hasRscPlugin = fs.existsSync(path.join(root, "node_modules", "@vitejs", "plugin-rsc"));
75
- const hasWrangler = fs.existsSync(path.join(root, "node_modules", ".bin", "wrangler"));
72
+ // Check node_modules for installed packages.
73
+ // Walk up ancestor directories so that monorepo-hoisted packages are found
74
+ // even when node_modules lives at the workspace root rather than app root.
75
+ const hasCloudflarePlugin = _findInNodeModules(root, "@cloudflare/vite-plugin") !== null;
76
+ const hasRscPlugin = _findInNodeModules(root, "@vitejs/plugin-rsc") !== null;
77
+ const hasWrangler = _findInNodeModules(root, ".bin/wrangler") !== null;
76
78
  // Derive project name from package.json or directory name
77
79
  let projectName = path.basename(root);
78
80
  const pkgPath = path.join(root, "package.json");
@@ -365,6 +367,7 @@ import {
365
367
  matchRewrite,
366
368
  matchHeaders,
367
369
  requestContextFromRequest,
370
+ applyMiddlewareRequestHeaders,
368
371
  isExternalUrl,
369
372
  proxyExternalRequest,
370
373
  sanitizeDestination,
@@ -384,6 +387,11 @@ interface Env {
384
387
  };
385
388
  }
386
389
 
390
+ interface ExecutionContext {
391
+ waitUntil(promise: Promise<unknown>): void;
392
+ passThroughOnException(): void;
393
+ }
394
+
387
395
  // Extract config values (embedded at build time in the server entry)
388
396
  const basePath: string = vinextConfig?.basePath ?? "";
389
397
  const trailingSlash: boolean = vinextConfig?.trailingSlash ?? false;
@@ -397,7 +405,7 @@ const imageConfig: ImageConfig | undefined = vinextConfig?.images ? {
397
405
  } : undefined;
398
406
 
399
407
  export default {
400
- async fetch(request: Request, env: Env): Promise<Response> {
408
+ async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
401
409
  try {
402
410
  const url = new URL(request.url);
403
411
  let pathname = url.pathname;
@@ -457,15 +465,19 @@ export default {
457
465
  }
458
466
 
459
467
  // Build request context for has/missing condition matching.
460
- // Created before middleware runs, matching prod-server ordering.
468
+ // headers and redirects run before middleware, so they use this
469
+ // pre-middleware snapshot. beforeFiles, afterFiles, and fallback
470
+ // rewrites run after middleware (App Router order), so they use
471
+ // postMwReqCtx created after x-middleware-request-* headers are
472
+ // unpacked into request.
461
473
  const reqCtx = requestContextFromRequest(request);
462
474
 
463
475
  // ── 3. Run middleware ──────────────────────────────────────────
464
476
  let resolvedUrl = urlWithQuery;
465
- const middlewareHeaders: Record<string, string> = {};
477
+ const middlewareHeaders: Record<string, string | string[]> = {};
466
478
  let middlewareRewriteStatus: number | undefined;
467
479
  if (typeof runMiddleware === "function") {
468
- const result = await runMiddleware(request);
480
+ const result = await runMiddleware(request, ctx);
469
481
 
470
482
  if (!result.continue) {
471
483
  if (result.redirectUrl) {
@@ -479,10 +491,22 @@ export default {
479
491
  }
480
492
  }
481
493
 
482
- // Collect middleware response headers to merge into final response
494
+ // Collect middleware response headers to merge into final response.
495
+ // Use an array for Set-Cookie to preserve multiple values.
483
496
  if (result.responseHeaders) {
484
497
  for (const [key, value] of result.responseHeaders) {
485
- middlewareHeaders[key] = value;
498
+ if (key === "set-cookie") {
499
+ const existing = middlewareHeaders[key];
500
+ if (Array.isArray(existing)) {
501
+ existing.push(value);
502
+ } else if (existing) {
503
+ middlewareHeaders[key] = [existing as string, value];
504
+ } else {
505
+ middlewareHeaders[key] = [value];
506
+ }
507
+ } else {
508
+ middlewareHeaders[key] = value;
509
+ }
486
510
  }
487
511
  }
488
512
 
@@ -495,49 +519,40 @@ export default {
495
519
  middlewareRewriteStatus = result.rewriteStatus;
496
520
  }
497
521
 
498
- // Unpack x-middleware-request-* headers into the actual request so
499
- // that renderPage / handleApiRoute see middleware-modified headers.
500
- // Workers incoming request headers are immutable, so clone if needed.
501
- const mwReqPrefix = "x-middleware-request-";
502
- const mwReqHeaders: Record<string, string> = {};
503
- for (const key of Object.keys(middlewareHeaders)) {
504
- if (key.startsWith(mwReqPrefix)) {
505
- const realName = key.slice(mwReqPrefix.length);
506
- mwReqHeaders[realName] = middlewareHeaders[key];
507
- delete middlewareHeaders[key];
508
- } else if (key.startsWith("x-middleware-")) {
509
- delete middlewareHeaders[key];
510
- }
511
- }
512
- if (Object.keys(mwReqHeaders).length > 0) {
513
- const newHeaders = new Headers(request.headers);
514
- for (const [k, v] of Object.entries(mwReqHeaders)) {
515
- newHeaders.set(k, v);
516
- }
517
- request = new Request(request.url, {
518
- method: request.method,
519
- headers: newHeaders,
520
- body: request.body,
521
- // @ts-expect-error -- duplex needed for streaming request bodies
522
- duplex: request.body ? "half" : undefined,
523
- });
524
- }
522
+ // Unpack x-middleware-request-* headers into the actual request and strip
523
+ // all x-middleware-* internal signals. Rebuilds postMwReqCtx for use by
524
+ // beforeFiles, afterFiles, and fallback config rules (which run after
525
+ // middleware per the Next.js execution order).
526
+ const { postMwReqCtx, request: postMwReq } = applyMiddlewareRequestHeaders(middlewareHeaders, request);
527
+ request = postMwReq;
525
528
 
526
529
  let resolvedPathname = resolvedUrl.split("?")[0];
527
530
 
528
531
  // ── 4. Apply custom headers from next.config.js ───────────────
529
532
  // Config headers are additive for multi-value headers (Vary,
530
533
  // Set-Cookie) and override for everything else. Vary values are
531
- // comma-joined per HTTP spec. Set-Cookie values are comma-joined
532
- // here because middlewareHeaders is a flat Record<string, string>;
533
- // mergeHeaders() downstream handles multi-value expansion.
534
+ // comma-joined per HTTP spec. Set-Cookie values are accumulated
535
+ // as arrays (RFC 6265 forbids comma-joining cookies).
536
+ // Middleware headers take precedence: skip config keys already set
537
+ // by middleware so middleware always wins for the same key.
534
538
  if (configHeaders.length) {
535
539
  const matched = matchHeaders(resolvedPathname, configHeaders, reqCtx);
536
540
  for (const h of matched) {
537
541
  const lk = h.key.toLowerCase();
538
- if ((lk === "vary" || lk === "set-cookie") && middlewareHeaders[lk]) {
542
+ if (lk === "set-cookie") {
543
+ const existing = middlewareHeaders[lk];
544
+ if (Array.isArray(existing)) {
545
+ existing.push(h.value);
546
+ } else if (existing) {
547
+ middlewareHeaders[lk] = [existing as string, h.value];
548
+ } else {
549
+ middlewareHeaders[lk] = [h.value];
550
+ }
551
+ } else if (lk === "vary" && middlewareHeaders[lk]) {
539
552
  middlewareHeaders[lk] += ", " + h.value;
540
- } else {
553
+ } else if (!(lk in middlewareHeaders)) {
554
+ // Middleware headers take precedence: only set if middleware
555
+ // did not already place this key on the response.
541
556
  middlewareHeaders[lk] = h.value;
542
557
  }
543
558
  }
@@ -561,7 +576,7 @@ export default {
561
576
 
562
577
  // ��─ 6. Apply beforeFiles rewrites from next.config.js ─────────
563
578
  if (configRewrites.beforeFiles?.length) {
564
- const rewritten = matchRewrite(resolvedPathname, configRewrites.beforeFiles, reqCtx);
579
+ const rewritten = matchRewrite(resolvedPathname, configRewrites.beforeFiles, postMwReqCtx);
565
580
  if (rewritten) {
566
581
  if (isExternalUrl(rewritten)) {
567
582
  return proxyExternalRequest(request, rewritten);
@@ -581,7 +596,7 @@ export default {
581
596
 
582
597
  // ── 8. Apply afterFiles rewrites from next.config.js ──────────
583
598
  if (configRewrites.afterFiles?.length) {
584
- const rewritten = matchRewrite(resolvedPathname, configRewrites.afterFiles, reqCtx);
599
+ const rewritten = matchRewrite(resolvedPathname, configRewrites.afterFiles, postMwReqCtx);
585
600
  if (rewritten) {
586
601
  if (isExternalUrl(rewritten)) {
587
602
  return proxyExternalRequest(request, rewritten);
@@ -598,7 +613,7 @@ export default {
598
613
 
599
614
  // ── 10. Fallback rewrites (if SSR returned 404) ─────────────
600
615
  if (response && response.status === 404 && configRewrites.fallback?.length) {
601
- const fallbackRewrite = matchRewrite(resolvedPathname, configRewrites.fallback, reqCtx);
616
+ const fallbackRewrite = matchRewrite(resolvedPathname, configRewrites.fallback, postMwReqCtx);
602
617
  if (fallbackRewrite) {
603
618
  if (isExternalUrl(fallbackRewrite)) {
604
619
  return proxyExternalRequest(request, fallbackRewrite);
@@ -622,19 +637,34 @@ export default {
622
637
 
623
638
  /**
624
639
  * Merge middleware/config headers into a response.
625
- * Response headers take precedence over middleware headers, matching
626
- * the behavior in prod-server.ts.
640
+ * Response headers take precedence over middleware headers for all headers
641
+ * except Set-Cookie, which is additive (both middleware and response cookies
642
+ * are preserved). Matches the behavior in prod-server.ts. Uses getSetCookie()
643
+ * to preserve multiple Set-Cookie values.
627
644
  */
628
645
  function mergeHeaders(
629
646
  response: Response,
630
- extraHeaders: Record<string, string>,
647
+ extraHeaders: Record<string, string | string[]>,
631
648
  statusOverride?: number,
632
649
  ): Response {
633
650
  if (!Object.keys(extraHeaders).length && !statusOverride) return response;
634
- // Middleware/config headers go in first (lower precedence), then
635
- // response headers overlay them (higher precedence).
636
- const merged: Record<string, string> = { ...extraHeaders };
637
- response.headers.forEach((v, k) => { merged[k] = v; });
651
+ const merged = new Headers();
652
+ // Middleware/config headers go in first (lower precedence)
653
+ for (const [k, v] of Object.entries(extraHeaders)) {
654
+ if (Array.isArray(v)) {
655
+ for (const item of v) merged.append(k, item);
656
+ } else {
657
+ merged.set(k, v);
658
+ }
659
+ }
660
+ // Response headers overlay them (higher precedence), except Set-Cookie
661
+ // which is additive (both middleware and response cookies should be sent).
662
+ response.headers.forEach((v, k) => {
663
+ if (k === "set-cookie") return;
664
+ merged.set(k, v);
665
+ });
666
+ const responseCookies = response.headers.getSetCookie?.() ?? [];
667
+ for (const cookie of responseCookies) merged.append("set-cookie", cookie);
638
668
  return new Response(response.body, {
639
669
  status: statusOverride ?? response.status,
640
670
  statusText: response.statusText,
@@ -752,8 +782,8 @@ _isResolvable = isPackageResolvable) {
752
782
  }
753
783
  }
754
784
  if (info.hasMDX) {
755
- // Check if @mdx-js/rollup is already installed
756
- const hasMdxRollup = fs.existsSync(path.join(info.root, "node_modules", "@mdx-js", "rollup"));
785
+ // Check if @mdx-js/rollup is already installed (walk up for monorepo hoisting)
786
+ const hasMdxRollup = _findInNodeModules(info.root, "@mdx-js/rollup") !== null;
757
787
  if (!hasMdxRollup) {
758
788
  missing.push({ name: "@mdx-js/rollup", version: "latest" });
759
789
  }
@@ -773,6 +803,35 @@ function installDeps(root, deps) {
773
803
  });
774
804
  }
775
805
  const detectPackageManager = _detectPackageManager;
806
+ /**
807
+ * Check whether an existing vite.config file already imports and uses the
808
+ * Cloudflare Vite plugin. This is a heuristic text scan — it doesn't execute
809
+ * the config — so it may produce false negatives for unusual configurations.
810
+ *
811
+ * Returns true if `@cloudflare/vite-plugin` appears to be configured, false
812
+ * if it is missing (meaning the build will fail with "could not resolve
813
+ * virtual:vinext-rsc-entry").
814
+ */
815
+ export function viteConfigHasCloudflarePlugin(root) {
816
+ const candidates = [
817
+ path.join(root, "vite.config.ts"),
818
+ path.join(root, "vite.config.js"),
819
+ path.join(root, "vite.config.mjs"),
820
+ ];
821
+ for (const candidate of candidates) {
822
+ if (fs.existsSync(candidate)) {
823
+ try {
824
+ const content = fs.readFileSync(candidate, "utf-8");
825
+ return content.includes("@cloudflare/vite-plugin");
826
+ }
827
+ catch {
828
+ // unreadable — assume it might be fine
829
+ return true;
830
+ }
831
+ }
832
+ }
833
+ return false;
834
+ }
776
835
  export function getFilesToGenerate(info) {
777
836
  const files = [];
778
837
  if (!info.hasWranglerConfig) {
@@ -840,7 +899,10 @@ export function buildWranglerDeployArgs(options) {
840
899
  return { args, env };
841
900
  }
842
901
  function runWranglerDeploy(root, options) {
843
- const wranglerBin = path.join(root, "node_modules", ".bin", "wrangler");
902
+ // Walk up ancestor directories so the binary is found even when node_modules
903
+ // is hoisted to the workspace root in a monorepo.
904
+ const wranglerBin = _findInNodeModules(root, ".bin/wrangler") ??
905
+ path.join(root, "node_modules", ".bin", "wrangler"); // fallback for error message clarity
844
906
  const execOpts = {
845
907
  cwd: root,
846
908
  stdio: "pipe",
@@ -925,6 +987,28 @@ export async function deploy(options) {
925
987
  console.log();
926
988
  writeGeneratedFiles(filesToGenerate);
927
989
  }
990
+ // Warn if an existing vite.config.ts is missing the Cloudflare plugin.
991
+ // This is the most common cause of "could not resolve virtual:vinext-rsc-entry"
992
+ // errors — `vinext init` generates a minimal local-dev config without it.
993
+ if (info.hasViteConfig && !viteConfigHasCloudflarePlugin(root)) {
994
+ console.warn(`
995
+ Warning: your vite.config.ts does not appear to import @cloudflare/vite-plugin.
996
+ Cloudflare Workers deployment requires it. Add the plugin to your config:
997
+
998
+ import { cloudflare } from "@cloudflare/vite-plugin";
999
+
1000
+ export default defineConfig({
1001
+ plugins: [
1002
+ vinext(),
1003
+ cloudflare(${info.isAppRouter ? `{
1004
+ viteEnvironment: { name: "rsc", childEnvironments: ["ssr"] },
1005
+ }` : ""}),
1006
+ ],
1007
+ });
1008
+
1009
+ Or delete vite.config.ts and re-run \`vinext deploy\` to auto-generate it.
1010
+ `);
1011
+ }
928
1012
  if (options.dryRun) {
929
1013
  console.log("\n Dry run complete. Files generated but no build or deploy performed.\n");
930
1014
  return;