vinext 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +2 -2
  2. package/dist/build/client-build-config.d.ts +4 -6
  3. package/dist/build/client-build-config.js +27 -7
  4. package/dist/build/google-fonts/find-font-files-in-css.d.ts +16 -0
  5. package/dist/build/google-fonts/find-font-files-in-css.js +28 -0
  6. package/dist/build/prerender.js +1 -2
  7. package/dist/build/report.d.ts +1 -2
  8. package/dist/build/report.js +2 -11
  9. package/dist/build/run-prerender.js +1 -1
  10. package/dist/check.js +3 -3
  11. package/dist/client/pages-router-link-navigation.d.ts +10 -0
  12. package/dist/client/pages-router-link-navigation.js +3 -3
  13. package/dist/deploy.js +3 -3
  14. package/dist/entries/app-rsc-entry.d.ts +2 -1
  15. package/dist/entries/app-rsc-entry.js +35 -9
  16. package/dist/entries/app-rsc-manifest.js +76 -46
  17. package/dist/entries/pages-server-entry.js +1 -0
  18. package/dist/index.js +116 -74
  19. package/dist/init.d.ts +5 -4
  20. package/dist/init.js +27 -8
  21. package/dist/plugins/fonts.js +3 -1
  22. package/dist/plugins/middleware-export-validation.d.ts +5 -0
  23. package/dist/plugins/middleware-export-validation.js +36 -0
  24. package/dist/plugins/og-asset-ownership.d.ts +27 -0
  25. package/dist/plugins/og-asset-ownership.js +285 -0
  26. package/dist/plugins/og-assets.js +28 -10
  27. package/dist/plugins/remove-console.js +1 -1
  28. package/dist/plugins/strip-server-exports.js +2 -1
  29. package/dist/server/accept-encoding.d.ts +29 -0
  30. package/dist/server/accept-encoding.js +95 -0
  31. package/dist/server/app-action-request.d.ts +4 -0
  32. package/dist/server/app-action-request.js +9 -0
  33. package/dist/server/app-bfcache-identity.d.ts +12 -2
  34. package/dist/server/app-bfcache-identity.js +52 -23
  35. package/dist/server/app-browser-entry.js +8 -5
  36. package/dist/server/app-browser-navigation-controller.js +1 -1
  37. package/dist/server/app-browser-rsc-redirect.d.ts +2 -1
  38. package/dist/server/app-browser-rsc-redirect.js +8 -1
  39. package/dist/server/app-browser-state.js +1 -1
  40. package/dist/server/app-browser-visible-commit.js +1 -1
  41. package/dist/server/app-history-state.d.ts +1 -2
  42. package/dist/server/app-history-state.js +1 -1
  43. package/dist/server/app-middleware.d.ts +1 -0
  44. package/dist/server/app-middleware.js +1 -0
  45. package/dist/server/app-page-dispatch.d.ts +2 -2
  46. package/dist/server/app-page-head.js +2 -2
  47. package/dist/server/app-page-method.js +1 -1
  48. package/dist/server/app-page-request.d.ts +7 -0
  49. package/dist/server/app-page-request.js +19 -1
  50. package/dist/server/app-page-stream.js +2 -1
  51. package/dist/server/app-prerender-endpoints.d.ts +3 -3
  52. package/dist/server/app-prerender-endpoints.js +3 -4
  53. package/dist/server/app-route-handler-cache.js +1 -1
  54. package/dist/server/app-route-handler-dispatch.js +2 -2
  55. package/dist/server/app-route-handler-execution.js +3 -2
  56. package/dist/server/app-route-handler-policy.d.ts +1 -1
  57. package/dist/server/app-route-handler-policy.js +1 -6
  58. package/dist/server/app-route-module-loader.d.ts +62 -14
  59. package/dist/server/app-route-module-loader.js +61 -10
  60. package/dist/server/app-rsc-errors.d.ts +12 -1
  61. package/dist/server/app-rsc-errors.js +22 -2
  62. package/dist/server/app-rsc-handler.d.ts +16 -14
  63. package/dist/server/app-rsc-handler.js +47 -44
  64. package/dist/server/app-rsc-response-finalizer.js +5 -1
  65. package/dist/server/app-rsc-route-matching.d.ts +9 -0
  66. package/dist/server/app-rsc-route-matching.js +13 -0
  67. package/dist/server/app-server-action-execution.js +1 -1
  68. package/dist/server/app-ssr-entry.js +9 -7
  69. package/dist/server/headers.d.ts +5 -1
  70. package/dist/server/headers.js +5 -1
  71. package/dist/server/metadata-route-response.d.ts +3 -3
  72. package/dist/server/middleware-runtime.d.ts +2 -1
  73. package/dist/server/middleware-runtime.js +19 -8
  74. package/dist/server/middleware.d.ts +2 -2
  75. package/dist/server/middleware.js +26 -11
  76. package/dist/server/pages-request-pipeline.d.ts +3 -1
  77. package/dist/server/pages-request-pipeline.js +4 -2
  78. package/dist/server/prod-server.d.ts +1 -1
  79. package/dist/server/prod-server.js +85 -72
  80. package/dist/shims/dynamic.js +1 -1
  81. package/dist/shims/font-google-base.d.ts +1 -0
  82. package/dist/shims/font-google-base.js +6 -21
  83. package/dist/shims/internal/interpolate-as.d.ts +25 -0
  84. package/dist/shims/internal/interpolate-as.js +196 -0
  85. package/dist/shims/link.js +24 -1
  86. package/dist/shims/navigation.js +1 -1
  87. package/dist/shims/navigation.react-server.js +1 -1
  88. package/dist/shims/router.d.ts +1 -3
  89. package/dist/shims/router.js +62 -18
  90. package/dist/typegen.js +2 -10
  91. package/dist/utils/project.d.ts +7 -1
  92. package/dist/utils/project.js +16 -2
  93. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import { normalizePath as normalizePath$1 } from "./server/normalize-path.js";
9
9
  import { matchesRewriteSource, proxyExternalRequest } from "./config/config-matchers.js";
10
10
  import { isOpenRedirectShaped } from "./server/open-redirect.js";
11
11
  import { filterInternalHeaders, normalizeTrailingSlash } from "./server/request-pipeline.js";
12
- import { findMiddlewareFile, runMiddleware } from "./server/middleware.js";
12
+ import { findMiddlewareFile, isProxyFile, runMiddleware } from "./server/middleware.js";
13
13
  import { generateServerEntry } from "./entries/pages-server-entry.js";
14
14
  import { generateClientEntry } from "./entries/pages-client-entry.js";
15
15
  import { appRouteGraph, appRouter, invalidateAppRouteCache, matchAppRoute } from "./routing/app-router.js";
@@ -52,6 +52,7 @@ import { createRscClientReferenceLoadersPlugin } from "./plugins/rsc-client-refe
52
52
  import { createInstrumentationClientTransformPlugin } from "./plugins/instrumentation-client.js";
53
53
  import { INSTRUMENTATION_CLIENT_EMPTY_MODULE, generateInstrumentationClientInjectModule } from "./client/instrumentation-client-inject.js";
54
54
  import { createMiddlewareServerOnlyPlugin } from "./plugins/middleware-server-only.js";
55
+ import { validateMiddlewareModuleExports } from "./plugins/middleware-export-validation.js";
55
56
  import { createOptimizeImportsPlugin } from "./plugins/optimize-imports.js";
56
57
  import { augmentSsrManifestFromBundle, relativeWithinRoot, tryRealpathSync } from "./build/ssr-manifest.js";
57
58
  import { createDynamicPreloadMetadataPlugin } from "./plugins/dynamic-preload-metadata.js";
@@ -331,7 +332,7 @@ function createStaticImageAsset(imagePath) {
331
332
  */
332
333
  const _shimsDir = normalizePathSeparators(path.resolve(__dirname, "shims")) + "/";
333
334
  const _fontGoogleShimPath = resolveShimModulePath(_shimsDir, "font-google");
334
- const _appRscHandlerPath = resolveShimModulePath(path.resolve(__dirname, "server"), "app-rsc-handler");
335
+ const _appRscHandlerPath = resolveShimModulePath(normalizePathSeparators(path.resolve(__dirname, "server")), "app-rsc-handler");
335
336
  const _canExternalizeAppRscHandler = _appRscHandlerPath.endsWith(".js");
336
337
  function isValidExportIdentifier(name) {
337
338
  return /^[$A-Z_a-z][$\w]*$/.test(name);
@@ -419,12 +420,16 @@ const _reactServerShims = new Map([
419
420
  ]);
420
421
  const clientManualChunks = createClientManualChunks(_shimsDir);
421
422
  const clientCodeSplittingConfig = createClientCodeSplittingConfig(clientManualChunks);
422
- function getClientOutputConfigForVite(viteMajorVersion, assetsDir) {
423
+ const appClientManualChunks = createClientManualChunks(_shimsDir, true);
424
+ const appClientCodeSplittingConfig = createClientCodeSplittingConfig(appClientManualChunks);
425
+ function getClientOutputConfigForVite(viteMajorVersion, assetsDir, preserveAppRouteBoundaries = false) {
426
+ const manualChunks = preserveAppRouteBoundaries ? appClientManualChunks : clientManualChunks;
427
+ const codeSplitting = preserveAppRouteBoundaries ? appClientCodeSplittingConfig : clientCodeSplittingConfig;
423
428
  return viteMajorVersion >= 8 ? {
424
429
  ...createClientFileNameConfig(assetsDir),
425
430
  assetFileNames: createClientAssetFileNames(assetsDir),
426
- codeSplitting: clientCodeSplittingConfig
427
- } : createClientOutputConfig(clientManualChunks, assetsDir);
431
+ codeSplitting
432
+ } : createClientOutputConfig(manualChunks, assetsDir);
428
433
  }
429
434
  function vinext(options = {}) {
430
435
  const viteMajorVersion = getViteMajorVersion();
@@ -449,7 +454,7 @@ function vinext(options = {}) {
449
454
  const sassComposesLoader = createSassAwareFileSystemLoader();
450
455
  const typeofWindowIdFilter = { exclude: /(?!)/ };
451
456
  let rscClassificationManifest = null;
452
- const shimsDir = path.resolve(__dirname, "shims");
457
+ const shimsDir = normalizePathSeparators(path.resolve(__dirname, "shims"));
453
458
  const canonicalize = (p) => normalizePathSeparators(tryRealpathSync(p) ?? p);
454
459
  const pageTransformCanonicalPaths = /* @__PURE__ */ new Map();
455
460
  const canonicalizePageTransformPath = (modulePath) => {
@@ -506,13 +511,16 @@ function vinext(options = {}) {
506
511
  let resolvedReactPath = null;
507
512
  let resolvedRscPath = null;
508
513
  let resolvedRscTransformsPath = null;
514
+ let rscPluginModulePromise = null;
509
515
  resolvedReactPath = resolveOptionalDependency(earlyBaseDir, "@vitejs/plugin-react");
510
516
  resolvedRscPath = resolveOptionalDependency(earlyBaseDir, "@vitejs/plugin-rsc");
511
517
  resolvedRscTransformsPath = resolveOptionalDependency(earlyBaseDir, "@vitejs/plugin-rsc/transforms");
512
518
  let rscPluginPromise = null;
513
519
  if (earlyAppDirExists && autoRsc) {
514
520
  if (!resolvedRscPath) throw new Error("vinext: App Router detected but @vitejs/plugin-rsc is not installed.\nRun: " + detectPackageManager(process.cwd()) + " @vitejs/plugin-rsc");
515
- rscPluginPromise = import(pathToFileURL(resolvedRscPath).href).then((mod) => {
521
+ const rscImport = import(pathToFileURL(resolvedRscPath).href);
522
+ rscPluginModulePromise = rscImport;
523
+ rscPluginPromise = rscImport.then((mod) => {
516
524
  const rsc = mod.default;
517
525
  return rsc({ entries: {
518
526
  rsc: VIRTUAL_RSC_ENTRY,
@@ -658,7 +666,8 @@ function vinext(options = {}) {
658
666
  fileMatcher = createValidFileMatcher(nextConfig.pageExtensions);
659
667
  instrumentationPath = findInstrumentationFile(root, fileMatcher);
660
668
  instrumentationClientPath = findInstrumentationClientFile(root, fileMatcher);
661
- middlewarePath = findMiddlewareFile(root, fileMatcher);
669
+ const middlewareConventionDir = canonicalize(baseDir) === canonicalize(path.posix.join(root, "src")) ? path.posix.join(root, "src") : root;
670
+ middlewarePath = findMiddlewareFile(root, fileMatcher, middlewareConventionDir);
662
671
  const instrumentationClientInjects = nextConfig.instrumentationClientInject.map((spec) => spec.startsWith("./") || spec.startsWith("../") ? path.resolve(root, spec) : spec);
663
672
  clientInjectModule = instrumentationClientInjects.length ? generateInstrumentationClientInjectModule(instrumentationClientInjects, instrumentationClientPath, INSTRUMENTATION_CLIENT_EMPTY_MODULE) : null;
664
673
  if (env?.command === "build") await writeRouteTypes();
@@ -721,55 +730,55 @@ function vinext(options = {}) {
721
730
  }
722
731
  for (const key of Object.keys(nextConfig.compilerDefineServer)) if (key in defines) throw new Error(`The \`compiler.defineServer\` option is configured to replace the \`${key}\` variable. This variable is either part of a built-in or is already configured.`);
723
732
  nextShimMap = Object.fromEntries(Object.entries({
724
- "next/link": path.join(shimsDir, "link"),
725
- "next/head": path.join(shimsDir, "head"),
726
- "next/router": path.join(shimsDir, "router"),
727
- "next/compat/router": path.join(shimsDir, "compat-router"),
728
- "next/image": path.join(shimsDir, "image"),
729
- "next/legacy/image": path.join(shimsDir, "legacy-image"),
730
- "next/dynamic": path.join(shimsDir, "dynamic"),
731
- "next/app": path.join(shimsDir, "app"),
732
- "next/document": path.join(shimsDir, "document"),
733
- "next/config": path.join(shimsDir, "config"),
734
- "next/script": path.join(shimsDir, "script"),
735
- "next/server": path.join(shimsDir, "server"),
736
- "next/headers": path.join(shimsDir, "headers"),
737
- "next/font/google": path.join(shimsDir, "font-google"),
738
- "next/font/local": path.join(shimsDir, "font-local"),
739
- "next/cache": path.join(shimsDir, "cache"),
740
- "next/form": path.join(shimsDir, "form"),
741
- "next/og": path.join(shimsDir, "og"),
742
- "next/web-vitals": path.join(shimsDir, "web-vitals"),
743
- "next/amp": path.join(shimsDir, "amp"),
744
- "next/offline": path.join(shimsDir, "offline"),
745
- "next/constants": path.join(shimsDir, "constants"),
746
- "next/dist/shared/lib/app-router-context.shared-runtime": path.join(shimsDir, "internal", "app-router-context"),
747
- "next/dist/shared/lib/app-router-context": path.join(shimsDir, "internal", "app-router-context"),
748
- "next/dist/shared/lib/router-context.shared-runtime": path.join(shimsDir, "internal", "router-context"),
749
- "next/dist/shared/lib/utils": path.join(shimsDir, "internal", "utils"),
750
- "next/dist/server/api-utils": path.join(shimsDir, "internal", "api-utils"),
751
- "next/dist/server/web/spec-extension/cookies": path.join(shimsDir, "internal", "cookies"),
752
- "next/dist/compiled/@edge-runtime/cookies": path.join(shimsDir, "internal", "cookies"),
753
- "next/dist/server/app-render/work-unit-async-storage.external": path.join(shimsDir, "internal", "work-unit-async-storage"),
754
- "next/dist/client/components/work-unit-async-storage.external": path.join(shimsDir, "internal", "work-unit-async-storage"),
755
- "next/dist/client/components/request-async-storage.external": path.join(shimsDir, "internal", "work-unit-async-storage"),
756
- "next/dist/client/components/request-async-storage": path.join(shimsDir, "internal", "work-unit-async-storage"),
757
- "next/dist/server/request/root-params": path.join(shimsDir, "root-params"),
758
- "next/dist/server/config-shared": path.join(shimsDir, "internal", "utils"),
759
- "server-only": path.join(shimsDir, "server-only"),
760
- "client-only": path.join(shimsDir, "client-only"),
761
- "vinext/error-boundary": path.join(shimsDir, "error-boundary"),
762
- "vinext/layout-segment-context": path.join(shimsDir, "layout-segment-context"),
763
- "vinext/metadata": path.join(shimsDir, "metadata"),
764
- "vinext/fetch-cache": path.join(shimsDir, "fetch-cache"),
765
- "vinext/cache-runtime": path.join(shimsDir, "cache-runtime"),
766
- "vinext/navigation-state": path.join(shimsDir, "navigation-state"),
767
- "vinext/unified-request-context": path.join(shimsDir, "unified-request-context"),
768
- "vinext/pages-router-runtime": path.join(shimsDir, "pages-router-runtime"),
769
- "vinext/router-state": path.join(shimsDir, "router-state"),
770
- "vinext/head-state": path.join(shimsDir, "head-state"),
771
- "vinext/i18n-state": path.join(shimsDir, "i18n-state"),
772
- "vinext/i18n-context": path.join(shimsDir, "i18n-context"),
733
+ "next/link": path.posix.join(shimsDir, "link"),
734
+ "next/head": path.posix.join(shimsDir, "head"),
735
+ "next/router": path.posix.join(shimsDir, "router"),
736
+ "next/compat/router": path.posix.join(shimsDir, "compat-router"),
737
+ "next/image": path.posix.join(shimsDir, "image"),
738
+ "next/legacy/image": path.posix.join(shimsDir, "legacy-image"),
739
+ "next/dynamic": path.posix.join(shimsDir, "dynamic"),
740
+ "next/app": path.posix.join(shimsDir, "app"),
741
+ "next/document": path.posix.join(shimsDir, "document"),
742
+ "next/config": path.posix.join(shimsDir, "config"),
743
+ "next/script": path.posix.join(shimsDir, "script"),
744
+ "next/server": path.posix.join(shimsDir, "server"),
745
+ "next/headers": path.posix.join(shimsDir, "headers"),
746
+ "next/font/google": path.posix.join(shimsDir, "font-google"),
747
+ "next/font/local": path.posix.join(shimsDir, "font-local"),
748
+ "next/cache": path.posix.join(shimsDir, "cache"),
749
+ "next/form": path.posix.join(shimsDir, "form"),
750
+ "next/og": path.posix.join(shimsDir, "og"),
751
+ "next/web-vitals": path.posix.join(shimsDir, "web-vitals"),
752
+ "next/amp": path.posix.join(shimsDir, "amp"),
753
+ "next/offline": path.posix.join(shimsDir, "offline"),
754
+ "next/constants": path.posix.join(shimsDir, "constants"),
755
+ "next/dist/shared/lib/app-router-context.shared-runtime": path.posix.join(shimsDir, "internal", "app-router-context"),
756
+ "next/dist/shared/lib/app-router-context": path.posix.join(shimsDir, "internal", "app-router-context"),
757
+ "next/dist/shared/lib/router-context.shared-runtime": path.posix.join(shimsDir, "internal", "router-context"),
758
+ "next/dist/shared/lib/utils": path.posix.join(shimsDir, "internal", "utils"),
759
+ "next/dist/server/api-utils": path.posix.join(shimsDir, "internal", "api-utils"),
760
+ "next/dist/server/web/spec-extension/cookies": path.posix.join(shimsDir, "internal", "cookies"),
761
+ "next/dist/compiled/@edge-runtime/cookies": path.posix.join(shimsDir, "internal", "cookies"),
762
+ "next/dist/server/app-render/work-unit-async-storage.external": path.posix.join(shimsDir, "internal", "work-unit-async-storage"),
763
+ "next/dist/client/components/work-unit-async-storage.external": path.posix.join(shimsDir, "internal", "work-unit-async-storage"),
764
+ "next/dist/client/components/request-async-storage.external": path.posix.join(shimsDir, "internal", "work-unit-async-storage"),
765
+ "next/dist/client/components/request-async-storage": path.posix.join(shimsDir, "internal", "work-unit-async-storage"),
766
+ "next/dist/server/request/root-params": path.posix.join(shimsDir, "root-params"),
767
+ "next/dist/server/config-shared": path.posix.join(shimsDir, "internal", "utils"),
768
+ "server-only": path.posix.join(shimsDir, "server-only"),
769
+ "client-only": path.posix.join(shimsDir, "client-only"),
770
+ "vinext/error-boundary": path.posix.join(shimsDir, "error-boundary"),
771
+ "vinext/layout-segment-context": path.posix.join(shimsDir, "layout-segment-context"),
772
+ "vinext/metadata": path.posix.join(shimsDir, "metadata"),
773
+ "vinext/fetch-cache": path.posix.join(shimsDir, "fetch-cache"),
774
+ "vinext/cache-runtime": path.posix.join(shimsDir, "cache-runtime"),
775
+ "vinext/navigation-state": path.posix.join(shimsDir, "navigation-state"),
776
+ "vinext/unified-request-context": path.posix.join(shimsDir, "unified-request-context"),
777
+ "vinext/pages-router-runtime": path.posix.join(shimsDir, "pages-router-runtime"),
778
+ "vinext/router-state": path.posix.join(shimsDir, "router-state"),
779
+ "vinext/head-state": path.posix.join(shimsDir, "head-state"),
780
+ "vinext/i18n-state": path.posix.join(shimsDir, "i18n-state"),
781
+ "vinext/i18n-context": path.posix.join(shimsDir, "i18n-context"),
773
782
  "vinext/cache": path.resolve(__dirname, "cache"),
774
783
  "vinext/instrumentation": path.resolve(__dirname, "server", "instrumentation"),
775
784
  "vinext/instrumentation-client": path.resolve(__dirname, "client", "instrumentation-client"),
@@ -888,6 +897,7 @@ function vinext(options = {}) {
888
897
  };
889
898
  const nextServerExternal = nextConfig?.serverExternalPackages ?? [];
890
899
  const userSsrExternal = Array.isArray(config.ssr?.external) ? [...config.ssr.external, ...nextServerExternal] : config.ssr?.external === true ? true : nextServerExternal;
900
+ const externalizeSsrReactInDev = env.command === "serve" && !hasCloudflarePlugin && !hasNitroPlugin;
891
901
  const incomingExclude = config.optimizeDeps?.exclude ?? [];
892
902
  const incomingInclude = config.optimizeDeps?.include ?? [];
893
903
  const depOptimizeAliasPlugin = {
@@ -942,11 +952,15 @@ function vinext(options = {}) {
942
952
  },
943
953
  ssr: {
944
954
  ...hasCloudflarePlugin || hasNitroPlugin ? {} : { resolve: {
945
- external: userSsrExternal === true ? true : [...userSsrExternal, "ipaddr.js"],
955
+ external: userSsrExternal === true ? true : [
956
+ ...userSsrExternal,
957
+ "ipaddr.js",
958
+ ...externalizeSsrReactInDev ? SSR_EXTERNAL_REACT_ENTRIES : []
959
+ ],
946
960
  ...userSsrExternal === true ? {} : { noExternal: true }
947
961
  } },
948
962
  optimizeDeps: {
949
- exclude: mergeOptimizeDepsExclude(incomingExclude, VINEXT_OPTIMIZE_DEPS_EXCLUDE, ["ipaddr.js"], userSsrExternal === true ? SSR_EXTERNAL_REACT_ENTRIES : []),
963
+ exclude: mergeOptimizeDepsExclude(incomingExclude, VINEXT_OPTIMIZE_DEPS_EXCLUDE, ["ipaddr.js"], userSsrExternal === true || externalizeSsrReactInDev ? SSR_EXTERNAL_REACT_ENTRIES : []),
950
964
  entries: optimizeEntries,
951
965
  ...depOptimizeNodeEnvOptions
952
966
  },
@@ -975,7 +989,7 @@ function vinext(options = {}) {
975
989
  assetsInlineLimit: clientAssetsInlineLimit,
976
990
  ...withBuildBundlerOptions(viteMajorVersion, {
977
991
  input: appClientInput,
978
- output: getClientOutputConfigForVite(viteMajorVersion, clientAssetsDir),
992
+ output: getClientOutputConfigForVite(viteMajorVersion, clientAssetsDir, true),
979
993
  treeshake: getClientTreeshakeConfigForVite(viteMajorVersion)
980
994
  })
981
995
  }
@@ -1125,6 +1139,12 @@ function vinext(options = {}) {
1125
1139
  if (id === RESOLVED_RSC_ENTRY && hasAppDir) {
1126
1140
  const routes = await appRouter(appDir, nextConfig?.pageExtensions, fileMatcher);
1127
1141
  const metaRoutes = scanMetadataFiles(appDir);
1142
+ let hasServerActions = true;
1143
+ if (this.environment?.config.command === "build" && rscPluginModulePromise) {
1144
+ const { getPluginApi } = await rscPluginModulePromise;
1145
+ const pluginApi = getPluginApi(this.environment.config);
1146
+ if (pluginApi && !pluginApi.manager.isScanBuild) hasServerActions = Object.keys(pluginApi.manager.serverReferenceMetaMap).length > 0;
1147
+ }
1128
1148
  const globalErrorPath = findFileWithExts(appDir, "global-error", fileMatcher);
1129
1149
  const globalNotFoundPath = findFileWithExts(appDir, "global-not-found", fileMatcher);
1130
1150
  rscClassificationManifest = collectRouteClassificationManifest(routes);
@@ -1144,6 +1164,7 @@ function vinext(options = {}) {
1144
1164
  cacheMaxMemorySize: nextConfig?.cacheMaxMemorySize,
1145
1165
  inlineCss: nextConfig?.inlineCss,
1146
1166
  cacheComponents: nextConfig?.cacheComponents,
1167
+ hasServerActions,
1147
1168
  i18n: nextConfig?.i18n,
1148
1169
  imageConfig: {
1149
1170
  deviceSizes: nextConfig?.images?.deviceSizes,
@@ -1302,15 +1323,19 @@ function vinext(options = {}) {
1302
1323
  const hook = mdxDelegate.config;
1303
1324
  return (typeof hook === "function" ? hook : hook.handler).call(this, config, env);
1304
1325
  },
1305
- async transform(code, id, options) {
1306
- if (id.includes("?")) return;
1307
- if (!id.toLowerCase().endsWith(".mdx")) return;
1308
- const delegate = mdxDelegate ?? await ensureMdxDelegate("on-demand");
1309
- if (delegate?.transform) {
1310
- const hook = delegate.transform;
1311
- return (typeof hook === "function" ? hook : hook.handler).call(this, code, id, options);
1326
+ transform: {
1327
+ filter: { id: {
1328
+ include: /\.mdx$/i,
1329
+ exclude: /\?/
1330
+ } },
1331
+ async handler(code, id, options) {
1332
+ const delegate = mdxDelegate ?? await ensureMdxDelegate("on-demand");
1333
+ if (delegate?.transform) {
1334
+ const hook = delegate.transform;
1335
+ return (typeof hook === "function" ? hook : hook.handler).call(this, code, id, options);
1336
+ }
1337
+ 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.`);
1312
1338
  }
1313
- 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
1339
  }
1315
1340
  },
1316
1341
  {
@@ -1356,6 +1381,7 @@ function vinext(options = {}) {
1356
1381
  configureServer(server) {
1357
1382
  const pageExtensions = fileMatcher.extensionRegex;
1358
1383
  let pagesRunner = null;
1384
+ let cachedSSRHandler = null;
1359
1385
  function getPagesRunner() {
1360
1386
  if (!pagesRunner) pagesRunner = createDirectRunner(server.environments["ssr"] ?? Object.values(server.environments).find((e) => e !== server.environments["rsc"]) ?? Object.values(server.environments)[0]);
1361
1387
  return pagesRunner;
@@ -1841,12 +1867,15 @@ function vinext(options = {}) {
1841
1867
  const appMatch = matchAppRoute(resolvedPathname, await appRouter(appDir, nextConfig?.pageExtensions, fileMatcher));
1842
1868
  if (appMatch && !pagesRouteHasPriorityOverAppRoute(renderMatch.route, appMatch.route)) return next();
1843
1869
  }
1844
- 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);
1870
+ if (!cachedSSRHandler || cachedSSRHandler.routes !== routes) cachedSSRHandler = {
1871
+ routes,
1872
+ 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)
1873
+ };
1845
1874
  flushStagedHeaders();
1846
1875
  flushRequestHeaders();
1847
1876
  if (pipelineResult.middlewareStatus !== void 0) req.__vinextMiddlewareStatus = pipelineResult.middlewareStatus;
1848
1877
  req.url = pipelineResult.resolvedUrl;
1849
- await handler(req, res, pipelineResult.resolvedUrl, req.__vinextMiddlewareStatus, pipelineResult.isDataReq, originalRequestUrl);
1878
+ await cachedSSRHandler.handler(req, res, pipelineResult.resolvedUrl, req.__vinextMiddlewareStatus, pipelineResult.isDataReq, originalRequestUrl);
1850
1879
  }
1851
1880
  } catch (e) {
1852
1881
  next(e);
@@ -1858,6 +1887,17 @@ function vinext(options = {}) {
1858
1887
  };
1859
1888
  }
1860
1889
  },
1890
+ {
1891
+ name: "vinext:validate-middleware-exports",
1892
+ enforce: "pre",
1893
+ transform(code, id) {
1894
+ if (!middlewarePath) return null;
1895
+ const modulePath = stripViteModuleQuery(id);
1896
+ if (canonicalize(modulePath) !== canonicalize(middlewarePath)) return null;
1897
+ validateMiddlewareModuleExports(code, modulePath, middlewarePath, isProxyFile(middlewarePath));
1898
+ return null;
1899
+ }
1900
+ },
1861
1901
  {
1862
1902
  name: "vinext:validate-page-exports",
1863
1903
  transform: {
@@ -2048,12 +2088,14 @@ function vinext(options = {}) {
2048
2088
  const varName = specifier.local?.name;
2049
2089
  if (!varName) continue;
2050
2090
  const dir = path.dirname(id);
2051
- const absImagePath = normalizePathSeparators(path.resolve(dir, importPath));
2091
+ const resolvedImage = importPath.startsWith(".") ? path.resolve(dir, importPath) : (await this.resolve(importPath, id, { skipSelf: true }))?.id;
2092
+ if (!resolvedImage) continue;
2093
+ const absImagePath = normalizePathSeparators(resolvedImage.split("?", 1)[0]);
2052
2094
  if (!fs.existsSync(absImagePath)) continue;
2053
2095
  imageImports.add(absImagePath);
2054
2096
  const urlVar = `__vinext_img_url_${varName}`;
2055
2097
  const metaVar = `__vinext_img_meta_${varName}`;
2056
- 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 };`;
2098
+ 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 };`;
2057
2099
  s.overwrite(importNode.start, importNode.end, replacement);
2058
2100
  hasChanges = true;
2059
2101
  }
@@ -2139,7 +2181,7 @@ function vinext(options = {}) {
2139
2181
  const { transformWrapExport, transformHoistInlineDirective } = await import(pathToFileURL(resolvedRscTransformsPath).href);
2140
2182
  if (cacheDirective) {
2141
2183
  const directiveValue = cacheDirective.expression.value;
2142
- const variant = directiveValue === "use cache" ? "" : directiveValue.replace("use cache:", "").replace("use cache: ", "").trim();
2184
+ const variant = directiveValue === "use cache" ? "" : directiveValue.replace("use cache:", "").trim();
2143
2185
  const isLayoutOrTemplate = /\/(layout|template)\.(tsx?|jsx?|mjs)$/.test(id);
2144
2186
  const modulePath = stripViteModuleQuery(id);
2145
2187
  const moduleFileName = path.basename(modulePath);
@@ -2179,7 +2221,7 @@ function vinext(options = {}) {
2179
2221
  directive: /^use cache(:\s*\w+)?$/,
2180
2222
  runtime: (value, name, meta) => {
2181
2223
  const directiveMatch = meta.directiveMatch[0];
2182
- const variant = directiveMatch === "use cache" ? "" : directiveMatch.replace("use cache:", "").replace("use cache: ", "").trim();
2224
+ const variant = directiveMatch === "use cache" ? "" : directiveMatch.replace("use cache:", "").trim();
2183
2225
  return `(await import(${JSON.stringify(runtimeModuleUrl2)})).registerCachedFunction(${value}, ${JSON.stringify(id + ":" + name)}, ${JSON.stringify(variant)})`;
2184
2226
  },
2185
2227
  rejectNonAsyncFunction: false
package/dist/init.d.ts CHANGED
@@ -10,7 +10,7 @@
10
10
  * 4. Rename CJS config files to .cjs
11
11
  * 5. Add vinext scripts to package.json
12
12
  * 6. Generate vite.config.ts
13
- * 7. Update .gitignore to include /dist/
13
+ * 7. Update .gitignore to include /dist/ and .vinext/
14
14
  * 8. Print summary
15
15
  *
16
16
  * Non-destructive: does NOT modify next.config, tsconfig, or source files.
@@ -32,7 +32,7 @@ type InitResult = {
32
32
  renamedConfigs: Array<[string, string]>; /** Whether scripts were added to package.json */
33
33
  addedScripts: string[]; /** Whether vite.config.ts was generated */
34
34
  generatedViteConfig: boolean; /** Whether vite.config.ts generation was skipped (already exists) */
35
- skippedViteConfig: boolean; /** Whether .gitignore was updated to include /dist/ */
35
+ skippedViteConfig: boolean; /** Whether .gitignore was updated to include vinext-generated output */
36
36
  updatedGitignore: boolean;
37
37
  };
38
38
  declare function generateViteConfig(_isAppRouter: boolean): string;
@@ -59,8 +59,9 @@ declare function isDepInstalled(root: string, dep: string): boolean;
59
59
  */
60
60
  declare function getReactUpgradeDeps(root: string): string[];
61
61
  /**
62
- * Ensure /dist/ is listed in .gitignore. Creates the file if it doesn't exist.
63
- * Returns true if the file was modified (or created), false if /dist/ was already present.
62
+ * Ensure vinext-generated output directories are listed in .gitignore.
63
+ * Creates the file if it doesn't exist. Returns true if the file was modified
64
+ * (or created), false if all entries were already present.
64
65
  */
65
66
  declare function updateGitignore(root: string): boolean;
66
67
  declare function init(options: InitOptions): Promise<InitResult>;
package/dist/init.js CHANGED
@@ -16,7 +16,7 @@ import { execFileSync } from "node:child_process";
16
16
  * 4. Rename CJS config files to .cjs
17
17
  * 5. Add vinext scripts to package.json
18
18
  * 6. Generate vite.config.ts
19
- * 7. Update .gitignore to include /dist/
19
+ * 7. Update .gitignore to include /dist/ and .vinext/
20
20
  * 8. Print summary
21
21
  *
22
22
  * Non-destructive: does NOT modify next.config, tsconfig, or source files.
@@ -140,20 +140,39 @@ function installDeps(root, deps, exec, { dev = true } = {}) {
140
140
  });
141
141
  }
142
142
  /**
143
- * Ensure /dist/ is listed in .gitignore. Creates the file if it doesn't exist.
144
- * Returns true if the file was modified (or created), false if /dist/ was already present.
143
+ * Ensure vinext-generated output directories are listed in .gitignore.
144
+ * Creates the file if it doesn't exist. Returns true if the file was modified
145
+ * (or created), false if all entries were already present.
145
146
  */
146
147
  function updateGitignore(root) {
147
148
  const gitignorePath = path.join(root, ".gitignore");
148
- const exactEntry = "/dist/";
149
+ const entries = [{
150
+ entry: "/dist/",
151
+ coveredBy: new Set([
152
+ "/dist/",
153
+ "/dist",
154
+ "dist/",
155
+ "dist"
156
+ ])
157
+ }, {
158
+ entry: ".vinext/",
159
+ coveredBy: new Set([
160
+ "/.vinext/",
161
+ "/.vinext",
162
+ ".vinext/",
163
+ ".vinext"
164
+ ])
165
+ }];
149
166
  let content = "";
167
+ let lines = [];
150
168
  if (fs.existsSync(gitignorePath)) {
151
169
  content = fs.readFileSync(gitignorePath, "utf-8");
152
- const lines = content.split("\n").map((l) => l.trim());
153
- if (lines.includes(exactEntry) || lines.includes("dist/") || lines.includes("dist")) return false;
170
+ lines = content.split("\n").map((l) => l.trim());
154
171
  }
172
+ const missingEntries = entries.filter(({ coveredBy }) => !lines.some((line) => coveredBy.has(line))).map(({ entry }) => entry);
173
+ if (missingEntries.length === 0) return false;
155
174
  const separator = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
156
- fs.writeFileSync(gitignorePath, content + separator + "/dist/\n", "utf-8");
175
+ fs.writeFileSync(gitignorePath, content + separator + missingEntries.join("\n") + "\n", "utf-8");
157
176
  return true;
158
177
  }
159
178
  async function init(options) {
@@ -212,7 +231,7 @@ async function init(options) {
212
231
  for (const script of addedScripts) console.log(` \u2713 Added ${script} script`);
213
232
  if (generatedViteConfig) console.log(` \u2713 Generated vite.config.ts`);
214
233
  if (skippedViteConfig) console.log(` - Skipped vite.config.ts (already exists, use --force to overwrite)`);
215
- if (updatedGitignore) console.log(` \u2713 Added /dist/ to .gitignore`);
234
+ if (updatedGitignore) console.log(` \u2713 Added vinext output directories to .gitignore`);
216
235
  console.log(`
217
236
  Next steps:
218
237
  ${pmName} run dev:vinext Start the vinext dev server
@@ -4,6 +4,7 @@ import { buildFallbackFontFace, getFallbackFontOverrideMetrics } from "../build/
4
4
  import { validateGoogleFontOptions } from "../build/google-fonts/validate.js";
5
5
  import { getFontAxes } from "../build/google-fonts/get-axes.js";
6
6
  import { buildGoogleFontsUrl } from "../build/google-fonts/build-url.js";
7
+ import { findFontFilesInCss } from "../build/google-fonts/find-font-files-in-css.js";
7
8
  import { ASSET_PREFIX_URL_DIR } from "../utils/asset-prefix.js";
8
9
  import { CONTENT_TYPES } from "../server/static-file-cache.js";
9
10
  import fs from "node:fs";
@@ -543,11 +544,12 @@ function createGoogleFontsPlugin(fontGoogleShimPath, shimsDir) {
543
544
  return;
544
545
  }
545
546
  const servedCSS = _rewriteCachedFontCssToServedUrls(localCSS, cacheDir, transformAssetsDir);
547
+ const preloadUrls = findFontFilesInCss(servedCSS, validated.preload ? validated.subsets : void 0).filter((file) => file.preloadFontFile).map((file) => file.googleFontFileUrl);
546
548
  const fallbackMetrics = validated.adjustFontFallback === false ? void 0 : getFallbackFontOverrideMetrics(family);
547
549
  const adjustedFallbackCSS = fallbackMetrics ? buildFallbackFontFace(family, fallbackMetrics) : void 0;
548
550
  const validatedFontWeight = validated.weights.length === 1 && validated.weights[0] !== "variable" ? Number(validated.weights[0]) : void 0;
549
551
  const validatedFontStyle = validated.styles.length === 1 ? validated.styles[0] : void 0;
550
- const internalFontProperties = [`selfHostedCSS: ${JSON.stringify(servedCSS)}`];
552
+ const internalFontProperties = [`selfHostedCSS: ${JSON.stringify(servedCSS)}`, `preloadUrls: ${JSON.stringify(preloadUrls)}`];
551
553
  if (adjustedFallbackCSS) internalFontProperties.push(`adjustedFallbackCSS: ${JSON.stringify(adjustedFallbackCSS)}`);
552
554
  if (Number.isFinite(validatedFontWeight)) internalFontProperties.push(`fontWeight: ${validatedFontWeight}`);
553
555
  if (validatedFontStyle) internalFontProperties.push(`fontStyle: ${JSON.stringify(validatedFontStyle)}`);
@@ -0,0 +1,5 @@
1
+ //#region src/plugins/middleware-export-validation.d.ts
2
+ declare function hasValidMiddlewareModuleExport(source: string, id: string, isProxy: boolean): boolean;
3
+ declare function validateMiddlewareModuleExports(source: string, id: string, filePath: string, isProxy: boolean): void;
4
+ //#endregion
5
+ export { hasValidMiddlewareModuleExport, validateMiddlewareModuleExports };
@@ -0,0 +1,36 @@
1
+ import { stripViteModuleQuery } from "../utils/path.js";
2
+ import { createMiddlewareMissingExportError } from "../server/middleware-runtime.js";
3
+ import { parseAst } from "vite";
4
+ //#region src/plugins/middleware-export-validation.ts
5
+ function parserLanguage(id) {
6
+ const cleanId = stripViteModuleQuery(id).toLowerCase();
7
+ if (cleanId.endsWith(".tsx")) return "tsx";
8
+ if (cleanId.endsWith(".ts") || cleanId.endsWith(".mts") || cleanId.endsWith(".cts")) return "ts";
9
+ return "jsx";
10
+ }
11
+ function astName(value) {
12
+ if (!value) return null;
13
+ if (typeof value.name === "string") return value.name;
14
+ if (typeof value.value === "string") return value.value;
15
+ return null;
16
+ }
17
+ function hasValidMiddlewareModuleExport(source, id, isProxy) {
18
+ const ast = parseAst(source, { lang: parserLanguage(id) });
19
+ const expectedExport = isProxy ? "proxy" : "middleware";
20
+ for (const statement of ast.body) {
21
+ if (statement.type === "ExportDefaultDeclaration") return true;
22
+ if (statement.type !== "ExportNamedDeclaration") continue;
23
+ const declaration = statement.declaration;
24
+ if (declaration?.type === "FunctionDeclaration" && astName(declaration.id) === expectedExport) return true;
25
+ if (declaration?.type === "VariableDeclaration") {
26
+ for (const declarator of declaration.declarations ?? []) if (astName(declarator.id) === expectedExport) return true;
27
+ }
28
+ for (const specifier of statement.specifiers ?? []) if (astName(specifier.exported ?? specifier.local) === expectedExport) return true;
29
+ }
30
+ return false;
31
+ }
32
+ function validateMiddlewareModuleExports(source, id, filePath, isProxy) {
33
+ if (!hasValidMiddlewareModuleExport(source, id, isProxy)) throw createMiddlewareMissingExportError(filePath, isProxy);
34
+ }
35
+ //#endregion
36
+ export { hasValidMiddlewareModuleExport, validateMiddlewareModuleExports };
@@ -0,0 +1,27 @@
1
+ import { Alias } from "vite";
2
+
3
+ //#region src/plugins/og-asset-ownership.d.ts
4
+ type OgAssetModuleBoundary = {
5
+ assetRoot: string;
6
+ moduleDir: string;
7
+ };
8
+ declare class OgAssetOwnership {
9
+ private projectRoot;
10
+ private readonly linkedPackageRoots;
11
+ private readonly dependencyPackageNames;
12
+ private readonly stringAliasesByFirstCharacter;
13
+ private regularExpressionAliases;
14
+ private configuredAliases;
15
+ configure(projectRoot: string, aliases: readonly Alias[]): void;
16
+ reset(): void;
17
+ shouldTrackImport(source: string): boolean;
18
+ recordResolvedImport(source: string, resolvedId: string): Promise<void>;
19
+ resolveModuleBoundary(moduleId: string): Promise<OgAssetModuleBoundary | null>;
20
+ resolveContainedAsset(assetRoot: string, assetPath: string): Promise<string | null>;
21
+ private findAlias;
22
+ private getExpectedPackageName;
23
+ private resolveAliasPackageRoot;
24
+ private resolveConfiguredAliasRoot;
25
+ }
26
+ //#endregion
27
+ export { OgAssetModuleBoundary, OgAssetOwnership };