vinext 0.1.6 → 0.1.8

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 (114) hide show
  1. package/dist/build/client-build-config.d.ts +4 -6
  2. package/dist/build/client-build-config.js +27 -7
  3. package/dist/build/prerender.js +1 -2
  4. package/dist/check.d.ts +18 -0
  5. package/dist/check.js +37 -17
  6. package/dist/cli.js +2 -2
  7. package/dist/config/next-config.d.ts +2 -1
  8. package/dist/config/next-config.js +3 -0
  9. package/dist/config/server-external-packages.d.ts +4 -0
  10. package/dist/config/server-external-packages.js +91 -0
  11. package/dist/deploy.d.ts +7 -0
  12. package/dist/deploy.js +25 -18
  13. package/dist/entries/app-rsc-entry.d.ts +2 -1
  14. package/dist/entries/app-rsc-entry.js +89 -20
  15. package/dist/entries/app-rsc-manifest.js +8 -0
  16. package/dist/entries/pages-client-entry.js +1 -0
  17. package/dist/entries/pages-server-entry.js +1 -0
  18. package/dist/index.js +87 -34
  19. package/dist/init.js +2 -1
  20. package/dist/plugins/middleware-server-only.d.ts +8 -6
  21. package/dist/plugins/middleware-server-only.js +8 -7
  22. package/dist/plugins/og-assets.js +6 -6
  23. package/dist/plugins/optimize-imports.js +1 -1
  24. package/dist/plugins/remove-console.js +1 -1
  25. package/dist/plugins/strip-server-exports.js +2 -1
  26. package/dist/routing/app-route-graph.d.ts +6 -2
  27. package/dist/routing/app-route-graph.js +61 -13
  28. package/dist/routing/app-router.d.ts +5 -0
  29. package/dist/routing/app-router.js +5 -0
  30. package/dist/routing/file-matcher.d.ts +5 -0
  31. package/dist/routing/file-matcher.js +7 -1
  32. package/dist/server/app-action-request.d.ts +4 -0
  33. package/dist/server/app-action-request.js +9 -0
  34. package/dist/server/app-bfcache-identity.d.ts +12 -2
  35. package/dist/server/app-bfcache-identity.js +52 -23
  36. package/dist/server/app-browser-entry.js +42 -147
  37. package/dist/server/app-browser-history-controller.d.ts +2 -1
  38. package/dist/server/app-browser-history-controller.js +6 -2
  39. package/dist/server/app-browser-server-action-client.d.ts +32 -0
  40. package/dist/server/app-browser-server-action-client.js +128 -0
  41. package/dist/server/app-fallback-renderer.d.ts +3 -1
  42. package/dist/server/app-fallback-renderer.js +6 -2
  43. package/dist/server/app-history-state.d.ts +1 -2
  44. package/dist/server/app-history-state.js +1 -1
  45. package/dist/server/app-page-boundary-render.d.ts +3 -0
  46. package/dist/server/app-page-boundary-render.js +14 -3
  47. package/dist/server/app-page-cache-finalizer.d.ts +1 -0
  48. package/dist/server/app-page-cache-finalizer.js +8 -2
  49. package/dist/server/app-page-dispatch.d.ts +33 -4
  50. package/dist/server/app-page-dispatch.js +62 -90
  51. package/dist/server/app-page-element-builder.d.ts +7 -1
  52. package/dist/server/app-page-element-builder.js +56 -19
  53. package/dist/server/app-page-head.d.ts +16 -1
  54. package/dist/server/app-page-head.js +44 -22
  55. package/dist/server/app-page-method.js +1 -1
  56. package/dist/server/app-page-params.d.ts +2 -1
  57. package/dist/server/app-page-params.js +8 -1
  58. package/dist/server/app-page-ppr-runtime.d.ts +7 -0
  59. package/dist/server/app-page-ppr-runtime.js +70 -0
  60. package/dist/server/app-page-probe.d.ts +1 -0
  61. package/dist/server/app-page-probe.js +1 -1
  62. package/dist/server/app-page-render.d.ts +4 -1
  63. package/dist/server/app-page-render.js +8 -3
  64. package/dist/server/app-page-request.d.ts +8 -1
  65. package/dist/server/app-page-request.js +23 -11
  66. package/dist/server/app-page-route-wiring.d.ts +6 -1
  67. package/dist/server/app-page-route-wiring.js +30 -8
  68. package/dist/server/app-page-search-params-observation.d.ts +4 -2
  69. package/dist/server/app-page-search-params-observation.js +11 -7
  70. package/dist/server/app-page-stream.js +2 -1
  71. package/dist/server/app-ppr-fallback-shell-render.d.ts +4 -3
  72. package/dist/server/app-route-handler-cache.js +1 -1
  73. package/dist/server/app-route-handler-dispatch.js +3 -2
  74. package/dist/server/app-route-handler-execution.js +5 -3
  75. package/dist/server/app-route-handler-policy.d.ts +1 -1
  76. package/dist/server/app-route-handler-policy.js +1 -6
  77. package/dist/server/app-route-module-loader.d.ts +2 -0
  78. package/dist/server/app-route-module-loader.js +1 -0
  79. package/dist/server/app-router-entry.js +7 -6
  80. package/dist/server/app-rsc-errors.d.ts +12 -1
  81. package/dist/server/app-rsc-errors.js +29 -3
  82. package/dist/server/app-rsc-handler.d.ts +15 -13
  83. package/dist/server/app-rsc-handler.js +28 -33
  84. package/dist/server/app-rsc-response-finalizer.js +5 -1
  85. package/dist/server/app-rsc-route-matching.d.ts +7 -0
  86. package/dist/server/app-rsc-route-matching.js +36 -3
  87. package/dist/server/app-segment-config.d.ts +1 -0
  88. package/dist/server/app-segment-config.js +32 -2
  89. package/dist/server/app-server-action-execution.d.ts +4 -0
  90. package/dist/server/app-server-action-execution.js +42 -11
  91. package/dist/server/app-ssr-entry.js +11 -6
  92. package/dist/server/app-static-generation.d.ts +1 -0
  93. package/dist/server/app-static-generation.js +1 -0
  94. package/dist/server/headers.d.ts +3 -1
  95. package/dist/server/headers.js +3 -1
  96. package/dist/server/metadata-route-response.d.ts +1 -1
  97. package/dist/server/prod-server.js +15 -6
  98. package/dist/server/worker-utils.d.ts +2 -1
  99. package/dist/server/worker-utils.js +7 -1
  100. package/dist/shims/dynamic.js +1 -1
  101. package/dist/shims/error-boundary.d.ts +19 -1
  102. package/dist/shims/error-boundary.js +11 -1
  103. package/dist/shims/headers.d.ts +3 -1
  104. package/dist/shims/headers.js +16 -5
  105. package/dist/shims/link.js +16 -12
  106. package/dist/shims/metadata.d.ts +3 -2
  107. package/dist/shims/metadata.js +8 -4
  108. package/dist/shims/router.js +13 -2
  109. package/dist/typegen.js +6 -5
  110. package/dist/utils/path.d.ts +2 -1
  111. package/dist/utils/path.js +1 -1
  112. package/dist/utils/project.d.ts +4 -0
  113. package/dist/utils/project.js +5 -1
  114. package/package.json +7 -3
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { detectPackageManager } from "./utils/project.js";
2
- import { normalizePathSeparators, stripJsExtension, stripViteModuleQuery } from "./utils/path.js";
3
2
  import { normalizePathnameForRouteMatchStrict } from "./routing/utils.js";
4
3
  import { escapeRegExp } from "./utils/regex.js";
4
+ import { normalizePathSeparators, stripJsExtension, stripViteModuleQuery } from "./utils/path.js";
5
5
  import { buildViteResolveExtensions, createValidFileMatcher, findFileWithExts, normalizeViteResolveExtensions } from "./routing/file-matcher.js";
6
6
  import { apiRouter, invalidateRouteCache, matchRoute, pagesRouter } from "./routing/pages-router.js";
7
7
  import { INTERNAL_HEADERS, NEXTJS_DEPLOYMENT_ID_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MW_CTX_HEADER, VINEXT_TIMING_HEADER } from "./server/headers.js";
@@ -35,6 +35,7 @@ import { resolveAssetsDir } from "./utils/asset-prefix.js";
35
35
  import { RESOLVED_VIRTUAL_GOOGLE_FONTS, VIRTUAL_GOOGLE_FONTS, createGoogleFontsPlugin, createLocalFontsPlugin, generateGoogleFontsVirtualModule, parseStaticObjectLiteral } from "./plugins/fonts.js";
36
36
  import { getDepOptimizeNodeEnvOptions, getViteMajorVersion, serializeViteDefine } from "./utils/vite-version.js";
37
37
  import { createRscCompatibilityId, findNextConfigPath, loadNextConfig, resolveNextConfig, resolveNextConfigInput } from "./config/next-config.js";
38
+ import { mergeServerExternalPackages } from "./config/server-external-packages.js";
38
39
  import { isNextDataPathname, parseNextDataPathname } from "./server/pages-data-route.js";
39
40
  import { precompressAssets } from "./build/precompress.js";
40
41
  import { ensureAssetsIgnore } from "./build/assets-ignore.js";
@@ -85,7 +86,6 @@ import { pathToFileURL } from "node:url";
85
86
  import { createHash, randomBytes, randomUUID } from "node:crypto";
86
87
  import commonjs from "vite-plugin-commonjs";
87
88
  import MagicString from "magic-string";
88
- import tsconfigPaths from "vite-tsconfig-paths";
89
89
  //#region src/index.ts
90
90
  installSocketErrorBackstop();
91
91
  function getCacheDirPrefix(cacheDir) {
@@ -138,6 +138,11 @@ function resolveOptionalDependency(projectRoot, specifier) {
138
138
  } catch {}
139
139
  return null;
140
140
  }
141
+ async function loadVite7TsconfigPathsPlugin(projectRoot) {
142
+ const resolvedPath = resolveOptionalDependency(projectRoot, "vite-tsconfig-paths");
143
+ if (!resolvedPath) throw new Error("[vinext] Vite 7 requires the optional peer dependency vite-tsconfig-paths for tsconfig path alias support. Install vite-tsconfig-paths or upgrade to Vite 8.");
144
+ return (await import(pathToFileURL(resolvedPath).href)).default();
145
+ }
141
146
  function resolveShimModulePath(shimsDir, moduleName) {
142
147
  for (const ext of [
143
148
  ".ts",
@@ -303,6 +308,8 @@ const VIRTUAL_APP_SSR_ENTRY = "virtual:vinext-app-ssr-entry";
303
308
  const RESOLVED_APP_SSR_ENTRY = "\0virtual:vinext-app-ssr-entry";
304
309
  const VIRTUAL_APP_BROWSER_ENTRY = "virtual:vinext-app-browser-entry";
305
310
  const RESOLVED_APP_BROWSER_ENTRY = "\0virtual:vinext-app-browser-entry";
311
+ const VIRTUAL_APP_CAPABILITIES = "virtual:vinext-app-capabilities";
312
+ const RESOLVED_APP_CAPABILITIES = "\0virtual:vinext-app-capabilities";
306
313
  const RESOLVED_ROOT_PARAMS = "\0virtual:vinext-root-params";
307
314
  /** Virtual module that registers config-driven cache adapters (see VinextOptions.cache). */
308
315
  const RESOLVED_CACHE_ADAPTERS = "\0" + VIRTUAL_CACHE_ADAPTERS;
@@ -332,6 +339,7 @@ function createStaticImageAsset(imagePath) {
332
339
  */
333
340
  const _shimsDir = normalizePathSeparators(path.resolve(__dirname, "shims")) + "/";
334
341
  const _fontGoogleShimPath = resolveShimModulePath(_shimsDir, "font-google");
342
+ const _appBrowserServerActionClientPath = resolveShimModulePath(normalizePathSeparators(path.resolve(__dirname, "server")), "app-browser-server-action-client");
335
343
  const _appRscHandlerPath = resolveShimModulePath(normalizePathSeparators(path.resolve(__dirname, "server")), "app-rsc-handler");
336
344
  const _canExternalizeAppRscHandler = _appRscHandlerPath.endsWith(".js");
337
345
  function isValidExportIdentifier(name) {
@@ -420,12 +428,16 @@ const _reactServerShims = new Map([
420
428
  ]);
421
429
  const clientManualChunks = createClientManualChunks(_shimsDir);
422
430
  const clientCodeSplittingConfig = createClientCodeSplittingConfig(clientManualChunks);
423
- function getClientOutputConfigForVite(viteMajorVersion, assetsDir) {
431
+ const appClientManualChunks = createClientManualChunks(_shimsDir, true);
432
+ const appClientCodeSplittingConfig = createClientCodeSplittingConfig(appClientManualChunks);
433
+ function getClientOutputConfigForVite(viteMajorVersion, assetsDir, preserveAppRouteBoundaries = false) {
434
+ const manualChunks = preserveAppRouteBoundaries ? appClientManualChunks : clientManualChunks;
435
+ const codeSplitting = preserveAppRouteBoundaries ? appClientCodeSplittingConfig : clientCodeSplittingConfig;
424
436
  return viteMajorVersion >= 8 ? {
425
437
  ...createClientFileNameConfig(assetsDir),
426
438
  assetFileNames: createClientAssetFileNames(assetsDir),
427
- codeSplitting: clientCodeSplittingConfig
428
- } : createClientOutputConfig(clientManualChunks, assetsDir);
439
+ codeSplitting
440
+ } : createClientOutputConfig(manualChunks, assetsDir);
429
441
  }
430
442
  function vinext(options = {}) {
431
443
  const viteMajorVersion = getViteMajorVersion();
@@ -507,13 +519,16 @@ function vinext(options = {}) {
507
519
  let resolvedReactPath = null;
508
520
  let resolvedRscPath = null;
509
521
  let resolvedRscTransformsPath = null;
522
+ let rscPluginModulePromise = null;
510
523
  resolvedReactPath = resolveOptionalDependency(earlyBaseDir, "@vitejs/plugin-react");
511
524
  resolvedRscPath = resolveOptionalDependency(earlyBaseDir, "@vitejs/plugin-rsc");
512
525
  resolvedRscTransformsPath = resolveOptionalDependency(earlyBaseDir, "@vitejs/plugin-rsc/transforms");
513
526
  let rscPluginPromise = null;
514
527
  if (earlyAppDirExists && autoRsc) {
515
528
  if (!resolvedRscPath) throw new Error("vinext: App Router detected but @vitejs/plugin-rsc is not installed.\nRun: " + detectPackageManager(process.cwd()) + " @vitejs/plugin-rsc");
516
- rscPluginPromise = import(pathToFileURL(resolvedRscPath).href).then((mod) => {
529
+ const rscImport = import(pathToFileURL(resolvedRscPath).href);
530
+ rscPluginModulePromise = rscImport;
531
+ rscPluginPromise = rscImport.then((mod) => {
517
532
  const rsc = mod.default;
518
533
  return rsc({ entries: {
519
534
  rsc: VIRTUAL_RSC_ENTRY,
@@ -524,6 +539,13 @@ function vinext(options = {}) {
524
539
  throw new Error("vinext: Failed to load @vitejs/plugin-rsc.", { cause });
525
540
  });
526
541
  }
542
+ async function resolveHasServerActions(config) {
543
+ if (config.command !== "build" || !rscPluginModulePromise) return true;
544
+ const { getPluginApi } = await rscPluginModulePromise;
545
+ const pluginApi = getPluginApi(config);
546
+ if (!pluginApi || pluginApi.manager.isScanBuild) return true;
547
+ return Object.keys(pluginApi.manager.serverReferenceMetaMap).length > 0;
548
+ }
527
549
  const reactOptions = options.react && options.react !== true ? options.react : void 0;
528
550
  let reactPluginPromise = null;
529
551
  if (options.react !== false) {
@@ -569,7 +591,7 @@ function vinext(options = {}) {
569
591
  return mdxDelegatePromise;
570
592
  }
571
593
  const plugins = [
572
- ...viteMajorVersion >= 8 ? [] : [tsconfigPaths()],
594
+ ...viteMajorVersion >= 8 ? [] : [loadVite7TsconfigPathsPlugin(earlyBaseDir)],
573
595
  reactPluginPromise,
574
596
  commonjs(),
575
597
  ...viteMajorVersion >= 8 ? [{
@@ -599,6 +621,10 @@ function vinext(options = {}) {
599
621
  createMiddlewareServerOnlyPlugin({
600
622
  getMiddlewarePath: () => middlewarePath,
601
623
  getCanonicalMiddlewarePath: () => middlewarePath ? tryRealpathSync(middlewarePath) ?? middlewarePath : null,
624
+ isNeutralServerModule: (id) => {
625
+ const canonicalId = canonicalizePageTransformPath(id);
626
+ return isWithinPagesDirectory(canonicalId) && isApiPage(canonicalId);
627
+ },
602
628
  serverOnlyShimPath: resolveShimModulePath(shimsDir, "server-only")
603
629
  }),
604
630
  dataUrlCssPlugin(),
@@ -674,6 +700,7 @@ function vinext(options = {}) {
674
700
  defines[`process.env.${key}`] = JSON.stringify(value);
675
701
  }
676
702
  defines["process.env.__NEXT_ROUTER_BASEPATH"] = JSON.stringify(nextConfig.basePath);
703
+ defines["process.env.__VINEXT_HAS_PAGES_ROUTER"] = JSON.stringify(String(hasPagesDir));
677
704
  defines["process.env.__NEXT_CLIENT_ROUTER_STATIC_STALETIME"] = JSON.stringify(String(nextConfig.staleTimes.static));
678
705
  defines["process.env.__VINEXT_PREFETCH_INLINING"] = JSON.stringify(nextConfig.prefetchInlining ? "true" : "false");
679
706
  defines["process.env.__NEXT_GESTURE_TRANSITION"] = JSON.stringify(nextConfig.gestureTransition);
@@ -793,6 +820,7 @@ function vinext(options = {}) {
793
820
  hasUserMdxPlugin = pluginsFlat.some((p) => p && typeof p === "object" && "name" in p && typeof p.name === "string" && (p.name === "@mdx-js/rollup" || p.name === "mdx"));
794
821
  if (!hasUserMdxPlugin && hasMdxFiles(root, hasAppDir ? appDir : null, hasPagesDir ? pagesDir : null)) await ensureMdxDelegate("detected");
795
822
  const isSSR = !!config.build?.ssr;
823
+ const nextServerExternal = mergeServerExternalPackages(nextConfig?.serverExternalPackages, nextConfig?.transpilePackages);
796
824
  const isMultiEnv = hasAppDir || hasCloudflarePlugin || hasNitroPlugin;
797
825
  const hasBuildInput = getBuildBundlerOptions(config.build)?.input !== void 0;
798
826
  const shouldInjectPlainPagesEnvironments = !hasAppDir && !hasCloudflarePlugin && !isSSR && !hasBuildInput;
@@ -843,7 +871,9 @@ function vinext(options = {}) {
843
871
  "react",
844
872
  "react-dom",
845
873
  "react-dom/server",
846
- "ipaddr.js"
874
+ "ipaddr.js",
875
+ ...Array.isArray(config.ssr?.external) ? config.ssr.external : [],
876
+ ...nextServerExternal
847
877
  ],
848
878
  noExternal: true
849
879
  } },
@@ -888,8 +918,8 @@ function vinext(options = {}) {
888
918
  ...cssModulesOverride
889
919
  }
890
920
  };
891
- const nextServerExternal = nextConfig?.serverExternalPackages ?? [];
892
921
  const userSsrExternal = Array.isArray(config.ssr?.external) ? [...config.ssr.external, ...nextServerExternal] : config.ssr?.external === true ? true : nextServerExternal;
922
+ const externalizeSsrReactInDev = env.command === "serve" && !hasCloudflarePlugin && !hasNitroPlugin;
893
923
  const incomingExclude = config.optimizeDeps?.exclude ?? [];
894
924
  const incomingInclude = config.optimizeDeps?.include ?? [];
895
925
  const depOptimizeAliasPlugin = {
@@ -944,11 +974,15 @@ function vinext(options = {}) {
944
974
  },
945
975
  ssr: {
946
976
  ...hasCloudflarePlugin || hasNitroPlugin ? {} : { resolve: {
947
- external: userSsrExternal === true ? true : [...userSsrExternal, "ipaddr.js"],
977
+ external: userSsrExternal === true ? true : [
978
+ ...userSsrExternal,
979
+ "ipaddr.js",
980
+ ...externalizeSsrReactInDev ? SSR_EXTERNAL_REACT_ENTRIES : []
981
+ ],
948
982
  ...userSsrExternal === true ? {} : { noExternal: true }
949
983
  } },
950
984
  optimizeDeps: {
951
- exclude: mergeOptimizeDepsExclude(incomingExclude, VINEXT_OPTIMIZE_DEPS_EXCLUDE, ["ipaddr.js"], userSsrExternal === true ? SSR_EXTERNAL_REACT_ENTRIES : []),
985
+ exclude: mergeOptimizeDepsExclude(incomingExclude, VINEXT_OPTIMIZE_DEPS_EXCLUDE, ["ipaddr.js"], userSsrExternal === true || externalizeSsrReactInDev ? SSR_EXTERNAL_REACT_ENTRIES : []),
952
986
  entries: optimizeEntries,
953
987
  ...depOptimizeNodeEnvOptions
954
988
  },
@@ -977,7 +1011,7 @@ function vinext(options = {}) {
977
1011
  assetsInlineLimit: clientAssetsInlineLimit,
978
1012
  ...withBuildBundlerOptions(viteMajorVersion, {
979
1013
  input: appClientInput,
980
- output: getClientOutputConfigForVite(viteMajorVersion, clientAssetsDir),
1014
+ output: getClientOutputConfigForVite(viteMajorVersion, clientAssetsDir, true),
981
1015
  treeshake: getClientTreeshakeConfigForVite(viteMajorVersion)
982
1016
  })
983
1017
  }
@@ -1019,7 +1053,8 @@ function vinext(options = {}) {
1019
1053
  "react",
1020
1054
  "react-dom",
1021
1055
  "react-dom/server",
1022
- "ipaddr.js"
1056
+ "ipaddr.js",
1057
+ ...nextServerExternal
1023
1058
  ],
1024
1059
  noExternal: true
1025
1060
  },
@@ -1090,7 +1125,7 @@ function vinext(options = {}) {
1090
1125
  resolveId: {
1091
1126
  filter: { id: /(?:next\/|vinext\/(?:shims\/|server\/app-rsc-handler)|virtual:vinext-|@vercel\/og(?:\.js)?$)/ },
1092
1127
  handler(id, importer) {
1093
- const cleanId = id.startsWith("\0") ? id.slice(1) : id;
1128
+ const cleanId = normalizePathSeparators(id.startsWith("\0") ? id.slice(1) : id);
1094
1129
  if (cleanId === "vinext/server/app-rsc-handler") {
1095
1130
  if (_canExternalizeAppRscHandler && this.environment?.name === "rsc" && this.environment.config?.command === "serve") return {
1096
1131
  id: _appRscHandlerPath,
@@ -1102,18 +1137,19 @@ function vinext(options = {}) {
1102
1137
  if (cleanId.startsWith("vinext/shims/")) return resolveShimModulePath(_shimsDir, stripJsExtension(stripViteModuleQuery(cleanId.slice(13))));
1103
1138
  if (cleanId === VIRTUAL_SERVER_ENTRY) return RESOLVED_SERVER_ENTRY;
1104
1139
  if (cleanId === VIRTUAL_CLIENT_ENTRY) return RESOLVED_CLIENT_ENTRY;
1105
- if (cleanId.endsWith("/virtual:vinext-server-entry") || cleanId.endsWith("\\virtual:vinext-server-entry")) return RESOLVED_SERVER_ENTRY;
1106
- if (cleanId.endsWith("/virtual:vinext-client-entry") || cleanId.endsWith("\\virtual:vinext-client-entry")) return RESOLVED_CLIENT_ENTRY;
1140
+ if (cleanId.endsWith("/virtual:vinext-server-entry")) return RESOLVED_SERVER_ENTRY;
1141
+ if (cleanId.endsWith("/virtual:vinext-client-entry")) return RESOLVED_CLIENT_ENTRY;
1107
1142
  if (cleanId === VIRTUAL_RSC_ENTRY) return RESOLVED_RSC_ENTRY;
1108
1143
  if (cleanId === VIRTUAL_APP_SSR_ENTRY) return RESOLVED_APP_SSR_ENTRY;
1109
1144
  if (cleanId === VIRTUAL_APP_BROWSER_ENTRY) return RESOLVED_APP_BROWSER_ENTRY;
1145
+ if (cleanId === VIRTUAL_APP_CAPABILITIES) return RESOLVED_APP_CAPABILITIES;
1110
1146
  if (cleanId === "next/root-params" || cleanId === "next/root-params.js") return RESOLVED_ROOT_PARAMS;
1111
- if (cleanId === "virtual:vinext-cache-adapters" || cleanId.endsWith("/virtual:vinext-cache-adapters") || cleanId.endsWith("\\virtual:vinext-cache-adapters")) return RESOLVED_CACHE_ADAPTERS;
1147
+ if (cleanId === "virtual:vinext-cache-adapters" || cleanId.endsWith("/virtual:vinext-cache-adapters")) return RESOLVED_CACHE_ADAPTERS;
1112
1148
  if (cleanId.startsWith("virtual:vinext-google-fonts?")) return RESOLVED_VIRTUAL_GOOGLE_FONTS + cleanId.slice(VIRTUAL_GOOGLE_FONTS.length);
1113
- if (cleanId.endsWith("/virtual:vinext-rsc-entry") || cleanId.endsWith("\\virtual:vinext-rsc-entry")) return RESOLVED_RSC_ENTRY;
1114
- if (cleanId.endsWith("/virtual:vinext-app-ssr-entry") || cleanId.endsWith("\\virtual:vinext-app-ssr-entry")) return RESOLVED_APP_SSR_ENTRY;
1115
- if (cleanId.endsWith("/virtual:vinext-app-browser-entry") || cleanId.endsWith("\\virtual:vinext-app-browser-entry")) return RESOLVED_APP_BROWSER_ENTRY;
1116
- if (cleanId.includes("/virtual:vinext-google-fonts?") || cleanId.includes("\\virtual:vinext-google-fonts?")) {
1149
+ if (cleanId.endsWith("/virtual:vinext-rsc-entry")) return RESOLVED_RSC_ENTRY;
1150
+ if (cleanId.endsWith("/virtual:vinext-app-ssr-entry")) return RESOLVED_APP_SSR_ENTRY;
1151
+ if (cleanId.endsWith("/virtual:vinext-app-browser-entry")) return RESOLVED_APP_BROWSER_ENTRY;
1152
+ if (cleanId.includes("/virtual:vinext-google-fonts?")) {
1117
1153
  const queryIndex = cleanId.indexOf(VIRTUAL_GOOGLE_FONTS + "?");
1118
1154
  return RESOLVED_VIRTUAL_GOOGLE_FONTS + cleanId.slice(queryIndex + VIRTUAL_GOOGLE_FONTS.length);
1119
1155
  }
@@ -1127,6 +1163,7 @@ function vinext(options = {}) {
1127
1163
  if (id === RESOLVED_RSC_ENTRY && hasAppDir) {
1128
1164
  const routes = await appRouter(appDir, nextConfig?.pageExtensions, fileMatcher);
1129
1165
  const metaRoutes = scanMetadataFiles(appDir);
1166
+ const hasServerActions = await resolveHasServerActions(this.environment.config);
1130
1167
  const globalErrorPath = findFileWithExts(appDir, "global-error", fileMatcher);
1131
1168
  const globalNotFoundPath = findFileWithExts(appDir, "global-not-found", fileMatcher);
1132
1169
  rscClassificationManifest = collectRouteClassificationManifest(routes);
@@ -1146,6 +1183,7 @@ function vinext(options = {}) {
1146
1183
  cacheMaxMemorySize: nextConfig?.cacheMaxMemorySize,
1147
1184
  inlineCss: nextConfig?.inlineCss,
1148
1185
  cacheComponents: nextConfig?.cacheComponents,
1186
+ hasServerActions,
1149
1187
  i18n: nextConfig?.i18n,
1150
1188
  imageConfig: {
1151
1189
  deviceSizes: nextConfig?.images?.deviceSizes,
@@ -1175,6 +1213,13 @@ function vinext(options = {}) {
1175
1213
  }))] : [];
1176
1214
  return generateBrowserEntry(graph.routes, graph.routeManifest, pagesPrefetchRoutes, nextConfig.rewrites);
1177
1215
  }
1216
+ if (id === RESOLVED_APP_CAPABILITIES && hasAppDir) {
1217
+ const hasServerActions = await resolveHasServerActions(this.environment.config);
1218
+ return `
1219
+ export const hasServerActions = ${JSON.stringify(hasServerActions)};
1220
+ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.stringify(_appBrowserServerActionClientPath)})` : "null"};
1221
+ `;
1222
+ }
1178
1223
  if (id.startsWith("\0virtual:vinext-google-fonts?")) return generateGoogleFontsVirtualModule(id, _fontGoogleShimPath);
1179
1224
  },
1180
1225
  // @vitejs/plugin-rsc runs the RSC environment build in two phases:
@@ -1304,15 +1349,19 @@ function vinext(options = {}) {
1304
1349
  const hook = mdxDelegate.config;
1305
1350
  return (typeof hook === "function" ? hook : hook.handler).call(this, config, env);
1306
1351
  },
1307
- async transform(code, id, options) {
1308
- if (id.includes("?")) return;
1309
- if (!id.toLowerCase().endsWith(".mdx")) return;
1310
- const delegate = mdxDelegate ?? await ensureMdxDelegate("on-demand");
1311
- if (delegate?.transform) {
1312
- const hook = delegate.transform;
1313
- return (typeof hook === "function" ? hook : hook.handler).call(this, code, id, options);
1352
+ transform: {
1353
+ filter: { id: {
1354
+ include: /\.mdx$/i,
1355
+ exclude: /\?/
1356
+ } },
1357
+ async handler(code, id, options) {
1358
+ const delegate = mdxDelegate ?? await ensureMdxDelegate("on-demand");
1359
+ if (delegate?.transform) {
1360
+ const hook = delegate.transform;
1361
+ return (typeof hook === "function" ? hook : hook.handler).call(this, code, id, options);
1362
+ }
1363
+ if (!hasUserMdxPlugin) throw new Error(`[vinext] Encountered MDX module ${id} but no MDX plugin is configured. Install @mdx-js/rollup or register an MDX plugin manually.`);
1314
1364
  }
1315
- if (!hasUserMdxPlugin) throw new Error(`[vinext] Encountered MDX module ${id} but no MDX plugin is configured. Install @mdx-js/rollup or register an MDX plugin manually.`);
1316
1365
  }
1317
1366
  },
1318
1367
  {
@@ -1358,6 +1407,7 @@ function vinext(options = {}) {
1358
1407
  configureServer(server) {
1359
1408
  const pageExtensions = fileMatcher.extensionRegex;
1360
1409
  let pagesRunner = null;
1410
+ let cachedSSRHandler = null;
1361
1411
  function getPagesRunner() {
1362
1412
  if (!pagesRunner) pagesRunner = createDirectRunner(server.environments["ssr"] ?? Object.values(server.environments).find((e) => e !== server.environments["rsc"]) ?? Object.values(server.environments)[0]);
1363
1413
  return pagesRunner;
@@ -1843,12 +1893,15 @@ function vinext(options = {}) {
1843
1893
  const appMatch = matchAppRoute(resolvedPathname, await appRouter(appDir, nextConfig?.pageExtensions, fileMatcher));
1844
1894
  if (appMatch && !pagesRouteHasPriorityOverAppRoute(renderMatch.route, appMatch.route)) return next();
1845
1895
  }
1846
- const handler = createSSRHandler(server, getPagesRunner(), routes, pagesDir, nextConfig?.i18n, fileMatcher, nextConfig?.basePath ?? "", nextConfig?.trailingSlash ?? false, middlewarePath !== null, (nextConfig?.rewrites.beforeFiles.length ?? 0) > 0 || (nextConfig?.rewrites.afterFiles.length ?? 0) > 0 || (nextConfig?.rewrites.fallback.length ?? 0) > 0, nextConfig?.clientTraceMetadata, nextConfig?.htmlLimitedBots);
1896
+ if (!cachedSSRHandler || cachedSSRHandler.routes !== routes) cachedSSRHandler = {
1897
+ routes,
1898
+ handler: createSSRHandler(server, getPagesRunner(), routes, pagesDir, nextConfig?.i18n, fileMatcher, nextConfig?.basePath ?? "", nextConfig?.trailingSlash ?? false, middlewarePath !== null, (nextConfig?.rewrites.beforeFiles.length ?? 0) > 0 || (nextConfig?.rewrites.afterFiles.length ?? 0) > 0 || (nextConfig?.rewrites.fallback.length ?? 0) > 0, nextConfig?.clientTraceMetadata, nextConfig?.htmlLimitedBots)
1899
+ };
1847
1900
  flushStagedHeaders();
1848
1901
  flushRequestHeaders();
1849
1902
  if (pipelineResult.middlewareStatus !== void 0) req.__vinextMiddlewareStatus = pipelineResult.middlewareStatus;
1850
1903
  req.url = pipelineResult.resolvedUrl;
1851
- await handler(req, res, pipelineResult.resolvedUrl, req.__vinextMiddlewareStatus, pipelineResult.isDataReq, originalRequestUrl);
1904
+ await cachedSSRHandler.handler(req, res, pipelineResult.resolvedUrl, req.__vinextMiddlewareStatus, pipelineResult.isDataReq, originalRequestUrl);
1852
1905
  }
1853
1906
  } catch (e) {
1854
1907
  next(e);
@@ -2068,7 +2121,7 @@ function vinext(options = {}) {
2068
2121
  imageImports.add(absImagePath);
2069
2122
  const urlVar = `__vinext_img_url_${varName}`;
2070
2123
  const metaVar = `__vinext_img_meta_${varName}`;
2071
- const replacement = `import ${urlVar} from ${JSON.stringify(absImagePath + "?vinext-image-url")};\nimport ${metaVar} from ${JSON.stringify(absImagePath + "?vinext-meta")};\nconst ${varName} = { src: ${urlVar}, width: ${metaVar}.width, height: ${metaVar}.height };`;
2124
+ const replacement = `import ${urlVar} from ${JSON.stringify(absImagePath + "?vinext-image-url")};\nimport ${metaVar} from ${JSON.stringify(absImagePath + "?vinext-meta")};\nvar ${varName} = { src: ${urlVar}, width: ${metaVar}.width, height: ${metaVar}.height };`;
2072
2125
  s.overwrite(importNode.start, importNode.end, replacement);
2073
2126
  hasChanges = true;
2074
2127
  }
@@ -2154,7 +2207,7 @@ function vinext(options = {}) {
2154
2207
  const { transformWrapExport, transformHoistInlineDirective } = await import(pathToFileURL(resolvedRscTransformsPath).href);
2155
2208
  if (cacheDirective) {
2156
2209
  const directiveValue = cacheDirective.expression.value;
2157
- const variant = directiveValue === "use cache" ? "" : directiveValue.replace("use cache:", "").replace("use cache: ", "").trim();
2210
+ const variant = directiveValue === "use cache" ? "" : directiveValue.replace("use cache:", "").trim();
2158
2211
  const isLayoutOrTemplate = /\/(layout|template)\.(tsx?|jsx?|mjs)$/.test(id);
2159
2212
  const modulePath = stripViteModuleQuery(id);
2160
2213
  const moduleFileName = path.basename(modulePath);
@@ -2194,7 +2247,7 @@ function vinext(options = {}) {
2194
2247
  directive: /^use cache(:\s*\w+)?$/,
2195
2248
  runtime: (value, name, meta) => {
2196
2249
  const directiveMatch = meta.directiveMatch[0];
2197
- const variant = directiveMatch === "use cache" ? "" : directiveMatch.replace("use cache:", "").replace("use cache: ", "").trim();
2250
+ const variant = directiveMatch === "use cache" ? "" : directiveMatch.replace("use cache:", "").trim();
2198
2251
  return `(await import(${JSON.stringify(runtimeModuleUrl2)})).registerCachedFunction(${value}, ${JSON.stringify(id + ":" + name)}, ${JSON.stringify(variant)})`;
2199
2252
  },
2200
2253
  rejectNonAsyncFunction: false
package/dist/init.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { detectPackageManager, detectPackageManagerName, ensureESModule, hasAppDir, hasViteConfig, renameCJSConfigs } from "./utils/project.js";
2
2
  import { formatReport, runCheck } from "./check.js";
3
+ import { normalizePathSeparators } from "./utils/path.js";
3
4
  import { createRequire } from "node:module";
4
5
  import fs from "node:fs";
5
6
  import path from "node:path";
@@ -176,7 +177,7 @@ function updateGitignore(root) {
176
177
  return true;
177
178
  }
178
179
  async function init(options) {
179
- const root = path.resolve(options.root);
180
+ const root = normalizePathSeparators(path.resolve(options.root));
180
181
  const port = options.port ?? 3001;
181
182
  const exec = options._exec ?? ((cmd, opts) => {
182
183
  const [program, ...args] = cmd.split(" ");
@@ -2,8 +2,8 @@ import { Plugin } from "vite";
2
2
 
3
3
  //#region src/plugins/middleware-server-only.d.ts
4
4
  /**
5
- * Allow `import 'server-only'` from middleware (and any module reachable
6
- * from middleware) in the SSR environment.
5
+ * Allow `import 'server-only'` from neutral server targets (and any module
6
+ * reachable from them) in the SSR environment.
7
7
  *
8
8
  * Background: middleware runs server-side, so importing `server-only` is
9
9
  * semantically correct. However, vinext bundles middleware into the SSR
@@ -13,9 +13,9 @@ import { Plugin } from "vite";
13
13
  *
14
14
  * 'server-only' cannot be imported in client build ('ssr' environment)
15
15
  *
16
- * Next.js solves this with webpack `issuerLayer` rules: middleware (and
17
- * `instrumentation`) sit in the `neutralTarget` layer where `server-only`
18
- * is aliased to a no-op while `client-only` still errors. See
16
+ * Next.js solves this with webpack `issuerLayer` rules: middleware,
17
+ * instrumentation, and Pages API routes sit in server-only or neutral layers
18
+ * where `server-only` is aliased to a no-op while `client-only` still errors. See
19
19
  * packages/next/src/build/webpack-config.ts ("Alias server-only and
20
20
  * client-only to proper exports based on bundling layers")
21
21
  *
@@ -23,7 +23,8 @@ import { Plugin } from "vite";
23
23
  * the behavior with import-chain taint tracking:
24
24
  *
25
25
  * 1. Seed a `tainted` set with the middleware entry path (and its
26
- * canonical realpath).
26
+ * canonical realpath), and recognize other neutral server entry modules
27
+ * such as Pages API routes through `isNeutralServerModule`.
27
28
  * 2. For every resolveId call from a tainted importer, resolve the import
28
29
  * via `this.resolve(..., { skipSelf: true })` and add the resolved id
29
30
  * to the tainted set. This propagates the taint along the import graph
@@ -47,6 +48,7 @@ import { Plugin } from "vite";
47
48
  declare function createMiddlewareServerOnlyPlugin(options: {
48
49
  getMiddlewarePath: () => string | null;
49
50
  getCanonicalMiddlewarePath: () => string | null;
51
+ isNeutralServerModule?: (id: string) => boolean;
50
52
  serverOnlyShimPath: string;
51
53
  }): Plugin;
52
54
  //#endregion
@@ -1,8 +1,8 @@
1
1
  import { normalizePathSeparators } from "../utils/path.js";
2
2
  //#region src/plugins/middleware-server-only.ts
3
3
  /**
4
- * Allow `import 'server-only'` from middleware (and any module reachable
5
- * from middleware) in the SSR environment.
4
+ * Allow `import 'server-only'` from neutral server targets (and any module
5
+ * reachable from them) in the SSR environment.
6
6
  *
7
7
  * Background: middleware runs server-side, so importing `server-only` is
8
8
  * semantically correct. However, vinext bundles middleware into the SSR
@@ -12,9 +12,9 @@ import { normalizePathSeparators } from "../utils/path.js";
12
12
  *
13
13
  * 'server-only' cannot be imported in client build ('ssr' environment)
14
14
  *
15
- * Next.js solves this with webpack `issuerLayer` rules: middleware (and
16
- * `instrumentation`) sit in the `neutralTarget` layer where `server-only`
17
- * is aliased to a no-op while `client-only` still errors. See
15
+ * Next.js solves this with webpack `issuerLayer` rules: middleware,
16
+ * instrumentation, and Pages API routes sit in server-only or neutral layers
17
+ * where `server-only` is aliased to a no-op while `client-only` still errors. See
18
18
  * packages/next/src/build/webpack-config.ts ("Alias server-only and
19
19
  * client-only to proper exports based on bundling layers")
20
20
  *
@@ -22,7 +22,8 @@ import { normalizePathSeparators } from "../utils/path.js";
22
22
  * the behavior with import-chain taint tracking:
23
23
  *
24
24
  * 1. Seed a `tainted` set with the middleware entry path (and its
25
- * canonical realpath).
25
+ * canonical realpath), and recognize other neutral server entry modules
26
+ * such as Pages API routes through `isNeutralServerModule`.
26
27
  * 2. For every resolveId call from a tainted importer, resolve the import
27
28
  * via `this.resolve(..., { skipSelf: true })` and add the resolved id
28
29
  * to the tainted set. This propagates the taint along the import graph
@@ -72,7 +73,7 @@ function createMiddlewareServerOnlyPlugin(options) {
72
73
  async handler(id, importer, opts) {
73
74
  if (this.environment?.name === "rsc") return;
74
75
  if (!importer) return;
75
- if (!isTainted(importer)) return;
76
+ if (!isTainted(importer) && !options.isNeutralServerModule?.(canonicalizeId(importer))) return;
76
77
  if (id === "server-only") return {
77
78
  id: options.serverOnlyShimPath,
78
79
  moduleSideEffects: false
@@ -46,7 +46,7 @@ function createOgInlineFetchAssetsPlugin() {
46
46
  const boundary = await ownership.resolveModuleBoundary(id);
47
47
  if (boundary === null) return null;
48
48
  const { assetRoot, moduleDir } = boundary;
49
- let newCode = code;
49
+ const s = new MagicString(code);
50
50
  let didReplace = false;
51
51
  const readAsBase64 = async (absPath) => {
52
52
  const realPath = await ownership.resolveContainedAsset(assetRoot, absPath);
@@ -75,22 +75,22 @@ function createOgInlineFetchAssetsPlugin() {
75
75
  `return Promise.resolve(a.buffer);`,
76
76
  `})()`
77
77
  ].join("");
78
- newCode = newCode.replaceAll(fullMatch, inlined);
78
+ s.overwrite(match.index, match.index + fullMatch.length, inlined);
79
79
  didReplace = true;
80
80
  }
81
- if (code.includes("readFileSync(")) for (const match of newCode.matchAll(/[a-zA-Z_$][a-zA-Z0-9_$]*\.readFileSync\(\s*(?:[a-zA-Z_$][a-zA-Z0-9_$]*\.)?fileURLToPath\(\s*new URL\(\s*(["'])(\.[^"']+)\1\s*,\s*import\.meta\.url\s*\)\s*\)\s*\)/g)) {
81
+ if (code.includes("readFileSync(")) for (const match of code.matchAll(/[a-zA-Z_$][a-zA-Z0-9_$]*\.readFileSync\(\s*(?:[a-zA-Z_$][a-zA-Z0-9_$]*\.)?fileURLToPath\(\s*new URL\(\s*(["'])(\.[^"']+)\1\s*,\s*import\.meta\.url\s*\)\s*\)\s*\)/g)) {
82
82
  const fullMatch = match[0];
83
83
  const relPath = match[2];
84
84
  const fileBase64 = await readAsBase64(path.resolve(moduleDir, relPath));
85
85
  if (fileBase64 === null) continue;
86
86
  const inlined = `Buffer.from(${JSON.stringify(fileBase64)},"base64")`;
87
- newCode = newCode.replaceAll(fullMatch, inlined);
87
+ s.overwrite(match.index, match.index + fullMatch.length, inlined);
88
88
  didReplace = true;
89
89
  }
90
90
  if (!didReplace) return null;
91
91
  return {
92
- code: newCode,
93
- map: null
92
+ code: s.toString(),
93
+ map: s.generateMap({ hires: "boundary" })
94
94
  };
95
95
  }
96
96
  }
@@ -1,5 +1,5 @@
1
- import { normalizePathSeparators } from "../utils/path.js";
2
1
  import { escapeRegExp } from "../utils/regex.js";
2
+ import { normalizePathSeparators } from "../utils/path.js";
3
3
  import { VIRTUAL_MODULE_ID_RE } from "../utils/virtual-module.js";
4
4
  import { getAstName } from "./ast-utils.js";
5
5
  import { createRequire } from "node:module";
@@ -32,7 +32,6 @@ const SCOPE_ENTERING_TYPES = new Set([
32
32
  */
33
33
  function removeConsoleCalls(code, config) {
34
34
  if (config === false) return null;
35
- const excluded = typeof config === "object" ? new Set(config.exclude.map((s) => s.toLowerCase())) : /* @__PURE__ */ new Set();
36
35
  if (!code.match(/\bconsole\b/)) return null;
37
36
  let ast;
38
37
  try {
@@ -40,6 +39,7 @@ function removeConsoleCalls(code, config) {
40
39
  } catch {
41
40
  return null;
42
41
  }
42
+ const excluded = typeof config === "object" ? new Set(config.exclude.map((s) => s.toLowerCase())) : /* @__PURE__ */ new Set();
43
43
  const scopeStack = [{ shadowed: false }];
44
44
  function currentScope() {
45
45
  return scopeStack[scopeStack.length - 1];
@@ -13,7 +13,8 @@ const SERVER_EXPORTS = new Set([
13
13
  const SERVER_PROPS_SSG_CONFLICT = "You can not use getStaticProps or getStaticPaths with getServerSideProps. To use SSG, please remove getServerSideProps";
14
14
  const EXPORT_ALL_IN_PAGE_ERROR = "Using `export * from '...'` in a page is disallowed. Please use `export { default } from '...'` instead.\nRead more: https://nextjs.org/docs/messages/export-all-in-page";
15
15
  function hasServerExportCandidate(code) {
16
- return [...SERVER_EXPORTS].some((name) => code.includes(name));
16
+ for (const name of SERVER_EXPORTS) if (code.includes(name)) return true;
17
+ return false;
17
18
  }
18
19
  function hasExportAllCandidate(code) {
19
20
  let searchFrom = 0;
@@ -23,7 +23,9 @@ type InterceptingRoute = {
23
23
  sourceMatchPattern: string; /** Absolute path to the intercepting page component */
24
24
  pagePath: string; /** Filesystem segments from app/ root to the intercepting page directory. */
25
25
  sourcePageSegments?: string[]; /** Absolute layout paths inside the intercepting route tree, outermost to innermost */
26
- layoutPaths: string[]; /** Parameter names for dynamic segments */
26
+ layoutPaths: string[]; /** Normalized branch segments accumulated at each intercept layout. */
27
+ layoutSegments?: string[][]; /** Full normalized interception branch segments through the page. */
28
+ branchSegments?: string[]; /** Parameter names for dynamic segments */
27
29
  params: string[];
28
30
  /**
29
31
  * Synthetic page-carrier slot id for sibling (slot-less) interception.
@@ -40,7 +42,9 @@ type ParallelSlot = {
40
42
  hasPage: boolean; /** Absolute path to the slot's page component */
41
43
  pagePath: string | null; /** Absolute path to the slot's default.tsx fallback */
42
44
  defaultPath: string | null; /** Absolute path to the slot's layout component (wraps slot content) */
43
- layoutPath: string | null; /** Absolute path to the slot's loading component */
45
+ layoutPath: string | null; /** Nested active-branch layouts whose exports contribute route config. */
46
+ configLayoutPaths?: string[]; /** Tree positions of configLayoutPaths relative to the slot root. */
47
+ configLayoutTreePositions?: number[]; /** Absolute path to the slot's loading component */
44
48
  loadingPath: string | null; /** Absolute path to the slot's error component */
45
49
  errorPath: string | null; /** Intercepting routes within this slot */
46
50
  interceptingRoutes: InterceptingRoute[];