vinext 1.4.1 → 1.4.3

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 (143) hide show
  1. package/README.md +5 -2
  2. package/dist/build/action-owner-manifest.js +1 -0
  3. package/dist/build/prerender-paths.d.ts +11 -1
  4. package/dist/build/prerender-paths.js +36 -10
  5. package/dist/check.js +1 -1
  6. package/dist/cli.js +1 -1
  7. package/dist/client/dev-error-overlay.d.ts +2 -1
  8. package/dist/client/dev-error-overlay.js +8 -3
  9. package/dist/config/config-matchers.js +8 -4
  10. package/dist/config/destination-params.js +5 -2
  11. package/dist/config/next-config.d.ts +25 -1
  12. package/dist/config/next-config.js +31 -1
  13. package/dist/entries/app-rsc-entry.js +65 -30
  14. package/dist/entries/app-rsc-manifest.js +4 -0
  15. package/dist/entries/pages-server-entry.js +19 -4
  16. package/dist/index.js +37 -37
  17. package/dist/init-cloudflare.js +18 -2
  18. package/dist/init-platform.js +2 -2
  19. package/dist/init.js +42 -5
  20. package/dist/plugins/css-target.d.ts +18 -0
  21. package/dist/plugins/css-target.js +93 -0
  22. package/dist/routing/app-route-graph.d.ts +10 -0
  23. package/dist/routing/app-route-graph.js +15 -1
  24. package/dist/server/api-handler.d.ts +2 -0
  25. package/dist/server/api-handler.js +10 -8
  26. package/dist/server/app-action-forwarding.js +3 -1
  27. package/dist/server/app-browser-entry.js +37 -41
  28. package/dist/server/app-browser-error.d.ts +1 -1
  29. package/dist/server/app-browser-error.js +13 -4
  30. package/dist/server/app-browser-supplemental-refresh.d.ts +13 -0
  31. package/dist/server/app-browser-supplemental-refresh.js +36 -4
  32. package/dist/server/app-elements.d.ts +3 -0
  33. package/dist/server/app-elements.js +14 -1
  34. package/dist/server/app-fallback-renderer.js +2 -2
  35. package/dist/server/app-middleware.d.ts +2 -0
  36. package/dist/server/app-middleware.js +1 -0
  37. package/dist/server/app-optimistic-routing.d.ts +12 -1
  38. package/dist/server/app-optimistic-routing.js +38 -16
  39. package/dist/server/app-page-boundary-render.js +4 -2
  40. package/dist/server/app-page-cache-render.js +1 -1
  41. package/dist/server/app-page-dispatch.d.ts +11 -1
  42. package/dist/server/app-page-dispatch.js +78 -31
  43. package/dist/server/app-page-element-builder.d.ts +11 -20
  44. package/dist/server/app-page-element-builder.js +39 -50
  45. package/dist/server/app-page-intercept-segment-config.d.ts +56 -0
  46. package/dist/server/app-page-intercept-segment-config.js +90 -0
  47. package/dist/server/app-page-navigation-params.d.ts +23 -0
  48. package/dist/server/app-page-navigation-params.js +49 -0
  49. package/dist/server/app-page-render.d.ts +5 -2
  50. package/dist/server/app-page-render.js +17 -3
  51. package/dist/server/app-page-request.d.ts +24 -2
  52. package/dist/server/app-page-request.js +10 -10
  53. package/dist/server/app-page-route-wiring.d.ts +17 -5
  54. package/dist/server/app-page-route-wiring.js +19 -13
  55. package/dist/server/app-page-segment-plan.js +9 -1
  56. package/dist/server/app-page-segment-state.d.ts +9 -0
  57. package/dist/server/app-page-segment-state.js +54 -1
  58. package/dist/server/app-pages-bridge.d.ts +6 -0
  59. package/dist/server/app-pages-bridge.js +8 -3
  60. package/dist/server/app-request-stage-independent-entry.js +2 -3
  61. package/dist/server/app-response-stage-entry.js +1 -0
  62. package/dist/server/app-route-module-loader.d.ts +2 -0
  63. package/dist/server/app-route-module-loader.js +1 -0
  64. package/dist/server/app-router-entry.js +2 -3
  65. package/dist/server/app-rsc-cache-busting.d.ts +8 -0
  66. package/dist/server/app-rsc-cache-busting.js +13 -1
  67. package/dist/server/app-rsc-combined-handler.js +2 -1
  68. package/dist/server/app-rsc-handler.d.ts +14 -1
  69. package/dist/server/app-rsc-handler.js +111 -62
  70. package/dist/server/app-rsc-response-finalizer.d.ts +18 -3
  71. package/dist/server/app-rsc-response-finalizer.js +61 -7
  72. package/dist/server/app-rsc-route-matching.d.ts +14 -0
  73. package/dist/server/app-rsc-route-matching.js +42 -18
  74. package/dist/server/app-server-action-execution.js +16 -10
  75. package/dist/server/app-ssr-entry.js +8 -0
  76. package/dist/server/cache-control.d.ts +2 -0
  77. package/dist/server/cache-control.js +3 -1
  78. package/dist/server/config-headers.d.ts +7 -0
  79. package/dist/server/config-headers.js +20 -1
  80. package/dist/server/image-animation.d.ts +9 -0
  81. package/dist/server/image-animation.js +118 -0
  82. package/dist/server/image-local-patterns.d.ts +21 -0
  83. package/dist/server/image-local-patterns.js +59 -0
  84. package/dist/server/image-optimization.d.ts +48 -27
  85. package/dist/server/image-optimization.js +316 -22
  86. package/dist/server/image-source-reader.d.ts +34 -0
  87. package/dist/server/image-source-reader.js +126 -0
  88. package/dist/server/metadata-route-response.d.ts +6 -1
  89. package/dist/server/metadata-route-response.js +46 -10
  90. package/dist/server/middleware-matcher.d.ts +1 -0
  91. package/dist/server/middleware-matcher.js +1 -1
  92. package/dist/server/middleware-runtime.d.ts +5 -0
  93. package/dist/server/middleware-runtime.js +48 -15
  94. package/dist/server/middleware.d.ts +1 -1
  95. package/dist/server/middleware.js +2 -1
  96. package/dist/server/nitro-public-files-plugin.js +20 -8
  97. package/dist/server/nitro-request-target-plugin.d.ts +17 -0
  98. package/dist/server/nitro-request-target-plugin.js +22 -0
  99. package/dist/server/open-redirect.d.ts +2 -1
  100. package/dist/server/open-redirect.js +2 -1
  101. package/dist/server/pages-client-assets.d.ts +1 -0
  102. package/dist/server/pages-data-route.d.ts +5 -1
  103. package/dist/server/pages-data-route.js +14 -6
  104. package/dist/server/pages-i18n.d.ts +7 -0
  105. package/dist/server/pages-i18n.js +14 -1
  106. package/dist/server/pages-node-compat.js +7 -3
  107. package/dist/server/pages-page-handler.js +4 -4
  108. package/dist/server/pages-request-pipeline.d.ts +9 -0
  109. package/dist/server/pages-request-pipeline.js +35 -15
  110. package/dist/server/pages-request-stage-entry.js +8 -2
  111. package/dist/server/pages-router-entry.js +3 -0
  112. package/dist/server/prod-server.d.ts +1 -1
  113. package/dist/server/prod-server.js +64 -62
  114. package/dist/server/request-pipeline.d.ts +5 -0
  115. package/dist/server/request-pipeline.js +11 -1
  116. package/dist/server/response-stage-cacheability.d.ts +7 -0
  117. package/dist/server/response-stage-cacheability.js +6 -5
  118. package/dist/server/srvx-middleware-origin.d.ts +13 -0
  119. package/dist/server/srvx-middleware-origin.js +19 -0
  120. package/dist/server/srvx-request-target.d.ts +12 -0
  121. package/dist/server/srvx-request-target.js +20 -0
  122. package/dist/server/static-file-cache.js +10 -22
  123. package/dist/server/worker-image-optimization.d.ts +10 -3
  124. package/dist/server/worker-image-optimization.js +43 -9
  125. package/dist/server/worker-revalidation-context.js +1 -0
  126. package/dist/server/worker-utils.d.ts +8 -0
  127. package/dist/server/worker-utils.js +19 -2
  128. package/dist/shims/error-boundary.d.ts +6 -2
  129. package/dist/shims/error-boundary.js +4 -2
  130. package/dist/shims/image-props.d.ts +6 -0
  131. package/dist/shims/image-props.js +39 -3
  132. package/dist/shims/image.js +3 -3
  133. package/dist/shims/request-context.d.ts +3 -1
  134. package/dist/shims/slot.d.ts +3 -0
  135. package/dist/shims/slot.js +67 -9
  136. package/dist/shims/url-utils.js +6 -1
  137. package/dist/utils/base-path.d.ts +7 -0
  138. package/dist/utils/base-path.js +11 -1
  139. package/dist/utils/client-entry-manifest.d.ts +5 -0
  140. package/dist/utils/client-entry-manifest.js +1 -0
  141. package/dist/utils/client-runtime-metadata.d.ts +5 -0
  142. package/dist/utils/client-runtime-metadata.js +33 -7
  143. package/package.json +1 -1
@@ -104,10 +104,13 @@ async function generatePagesRequestEntry(pagesDir, nextConfig, fileMatcher, midd
104
104
  deviceSizes: nextConfig?.images?.deviceSizes,
105
105
  imageSizes: nextConfig?.images?.imageSizes,
106
106
  qualities: nextConfig?.images?.qualities,
107
+ localPatterns: nextConfig?.images?.localPatterns,
107
108
  dangerouslyAllowSVG: nextConfig?.images?.dangerouslyAllowSVG,
108
109
  dangerouslyAllowLocalIP: nextConfig?.images?.dangerouslyAllowLocalIP,
109
110
  contentDispositionType: nextConfig?.images?.contentDispositionType,
110
- contentSecurityPolicy: nextConfig?.images?.contentSecurityPolicy
111
+ contentSecurityPolicy: nextConfig?.images?.contentSecurityPolicy,
112
+ minimumCacheTTL: nextConfig?.images?.minimumCacheTTL,
113
+ maximumResponseBody: nextConfig?.images?.maximumResponseBody
111
114
  }
112
115
  });
113
116
  const instrumentationImportCode = instrumentationPath ? `import * as _instrumentation from ${JSON.stringify(instrumentationPath)};
@@ -134,6 +137,7 @@ export function __ensureInstrumentation(ctx) {
134
137
  isProxy: ${JSON.stringify(isProxyFile(middlewarePath))},
135
138
  module: middlewareModule,
136
139
  request,
140
+ restoreLocaleRootTrailingSlash: options?.restoreLocaleRootTrailingSlash === true,
137
141
  skipTrailingSlashRedirect: vinextConfig.skipTrailingSlashRedirect,
138
142
  trailingSlash: vinextConfig.trailingSlash,
139
143
  });
@@ -164,11 +168,14 @@ export const vinextConfig = ${vinextConfigJson};
164
168
  export const publicFiles = new Set(${JSON.stringify(publicFiles)});
165
169
  export const clientBuildFiles = new Set(__pagesClientAssets.clientBuildFiles ?? []);
166
170
 
171
+ // The Worker request stage strips basePath before calling this, so the data
172
+ // path must not lose it again: with basePath "/docs", "/docs/docs/_next/data/..."
173
+ // is the page path "/docs/_next/data/...", not a data request.
167
174
  export function normalizeDataRequest(request) {
168
175
  return __normalizePagesDataRequest(
169
176
  request,
170
177
  buildId,
171
- vinextConfig.basePath,
178
+ "",
172
179
  __shouldAddTrailingSlashToPagesDataPath(
173
180
  hasMiddleware,
174
181
  vinextConfig.trailingSlash,
@@ -281,10 +288,13 @@ async function generateServerEntry(pagesDir, nextConfig, fileMatcher, middleware
281
288
  deviceSizes: nextConfig?.images?.deviceSizes,
282
289
  imageSizes: nextConfig?.images?.imageSizes,
283
290
  qualities: nextConfig?.images?.qualities,
291
+ localPatterns: nextConfig?.images?.localPatterns,
284
292
  dangerouslyAllowSVG: nextConfig?.images?.dangerouslyAllowSVG,
285
293
  dangerouslyAllowLocalIP: nextConfig?.images?.dangerouslyAllowLocalIP,
286
294
  contentDispositionType: nextConfig?.images?.contentDispositionType,
287
- contentSecurityPolicy: nextConfig?.images?.contentSecurityPolicy
295
+ contentSecurityPolicy: nextConfig?.images?.contentSecurityPolicy,
296
+ minimumCacheTTL: nextConfig?.images?.minimumCacheTTL,
297
+ maximumResponseBody: nextConfig?.images?.maximumResponseBody
288
298
  }
289
299
  });
290
300
  const instrumentationImportCode = instrumentationPath ? `import * as _instrumentation from ${JSON.stringify(instrumentationPath)};
@@ -331,6 +341,7 @@ export async function runMiddleware(request, ctx, options) {
331
341
  isProxy: ${JSON.stringify(isProxyFile(middlewarePath))},
332
342
  module: middlewareModule,
333
343
  request,
344
+ restoreLocaleRootTrailingSlash: options?.restoreLocaleRootTrailingSlash === true,
334
345
  skipTrailingSlashRedirect: vinextConfig.skipTrailingSlashRedirect,
335
346
  trailingSlash: vinextConfig.trailingSlash,
336
347
  });
@@ -398,11 +409,14 @@ export const buildId = ${buildIdJson};
398
409
  // Per-build capability used by Worker entries to authorize remote path
399
410
  // discovery. It is never included in responses or exposed to user modules.
400
411
  export const prerenderSecret = ${JSON.stringify(prerenderSecret ?? null)};
412
+ // The Worker request stage strips basePath before calling this, so the data
413
+ // path must not lose it again: with basePath "/docs", "/docs/docs/_next/data/..."
414
+ // is the page path "/docs/_next/data/...", not a data request.
401
415
  export function normalizeDataRequest(request) {
402
416
  return __normalizePagesDataRequest(
403
417
  request,
404
418
  buildId,
405
- vinextConfig.basePath,
419
+ "",
406
420
  __shouldAddTrailingSlashToPagesDataPath(
407
421
  hasMiddleware,
408
422
  vinextConfig.trailingSlash,
@@ -437,6 +451,7 @@ export const pageRoutes = [
437
451
  ${pageRouteEntries.join(",\n")}
438
452
  ];
439
453
  export const publicFiles = new Set(${JSON.stringify(publicFiles)});
454
+ export const clientBuildFiles = new Set(__pagesClientAssets.clientBuildFiles ?? []);
440
455
  const _pageRouteTrie = _buildRouteTrie(pageRoutes);
441
456
  const _errorPageRoute = {
442
457
  pattern: "/_error",
package/dist/index.js CHANGED
@@ -14,12 +14,12 @@ import { PHASE_DEVELOPMENT_SERVER, PHASE_PRODUCTION_BUILD } from "./shims/consta
14
14
  import { serializeCacheLifeProfiles } from "./utils/cache-life-profiles.js";
15
15
  import { VINEXT_MW_CTX_HEADER } from "./utils/protocol-headers.js";
16
16
  import { encodeMiddlewareRequestHeaders } from "./utils/middleware-request-headers.js";
17
- import { hasBasePath, stripBasePath } from "./utils/base-path.js";
17
+ import { hasBasePath, restoreBasePath, stripBasePath } from "./utils/base-path.js";
18
18
  import { INTERNAL_HEADERS, NEXTJS_DEPLOYMENT_ID_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_TIMING_HEADER } from "./server/headers.js";
19
19
  import { methodNotAllowedResponse, notFoundStaticAssetResponse } from "./server/http-error-responses.js";
20
20
  import { isOpenRedirectShaped, sendRepeatedSlashRedirect } from "./server/open-redirect.js";
21
21
  import { canonicalizeRequestUrlPathname, filterInternalHeaders, normalizeTrailingSlash } from "./server/request-pipeline.js";
22
- import { matchesRewriteSource, proxyExternalRequest } from "./config/config-matchers.js";
22
+ import { proxyExternalRequest } from "./config/config-matchers.js";
23
23
  import { omitUnusedBuildSourcemap } from "./plugins/transform-result.js";
24
24
  import { assetPrefixPathname, isNextStaticPath, resolveAssetsDir } from "./utils/asset-prefix.js";
25
25
  import { RESOLVED_VIRTUAL_GOOGLE_FONTS, VIRTUAL_GOOGLE_FONTS, createGoogleFontsPlugin, createLocalFontsPlugin, generateGoogleFontsVirtualModule, parseStaticObjectLiteral } from "./plugins/fonts.js";
@@ -31,7 +31,7 @@ import { apiRouter, invalidateRouteCache, matchRoute, pagesRouter } from "./rout
31
31
  import { resolveRuntimeEntryModule } from "./entries/runtime-entry-module.js";
32
32
  import { normalizePath as normalizePath$1 } from "./server/normalize-path.js";
33
33
  import { validateMiddlewareMatcherPatterns } from "./server/middleware-matcher-pattern.js";
34
- import { resolvePagesI18nRequest, shouldRestoreLocaleRootTrailingSlash, stripI18nLocaleForApiRoute } from "./server/pages-i18n.js";
34
+ import { resolvePagesI18nRequest, shouldRestoreLocaleRootTrailingSlash } from "./server/pages-i18n.js";
35
35
  import { encodeUrlParserIgnoredCharacters, isNextDataPathname, normalizeNextDataPagePathname, parseNextDataPathname, shouldAddTrailingSlashToPagesDataPath, urlParserCreatesPagesDataPath } from "./server/pages-data-route.js";
36
36
  import { findInstrumentationClientFile, findInstrumentationFile, runInstrumentation } from "./server/instrumentation.js";
37
37
  import { findMiddlewareFile, isProxyFile, runMiddleware } from "./server/middleware.js";
@@ -78,6 +78,7 @@ import { readTrustedRevalidationHostname } from "./server/revalidation-host.js";
78
78
  import { sealForwardedMiddlewareContext } from "./server/forwarded-middleware-context.js";
79
79
  import { installDevStackSourcemapMiddleware } from "./server/dev-stack-sourcemap.js";
80
80
  import { traceFrameworkRequest } from "./server/request-tracing.js";
81
+ import { publicFileRouteKey } from "./utils/public-file-path.js";
81
82
  import { runPagesRequest } from "./server/pages-request-pipeline.js";
82
83
  import { pagesRouteHasPriorityOverAppRoute, validateHybridRouteConflicts } from "./server/hybrid-route-priority.js";
83
84
  import { hasReactCompilerPlugin, isReactCompilerPlugin, isReactCompilerRequested, reactCompilerUnsupportedMessage } from "./utils/react-compiler-support.js";
@@ -89,6 +90,7 @@ import { createClientCompileHintsPlugin } from "./plugins/client-compile-hints.j
89
90
  import { clientReferenceDedupPlugin } from "./plugins/client-reference-dedup.js";
90
91
  import { dataUrlCssPlugin } from "./plugins/css-data-url.js";
91
92
  import { createCssModuleImportCompatibilityPlugin } from "./plugins/css-module-imports.js";
93
+ import { createCssTargetPlugin } from "./plugins/css-target.js";
92
94
  import { createWorkerImageImportsPlugin } from "./plugins/worker-image-imports.js";
93
95
  import { assertClientOutDirIsSeparate, createClientAssetFileNames, createClientCodeSplittingConfig, createClientManualChunks, createClientOutputFileNameDefaults, createMultiStageChunkFileNames, createMultiStageCodeSplittingConfig, createRscFrameworkChunkOutputConfig, createWorkerFileNames, findUnsupportedClientOutputFileNames, getBuildBundlerOptions, getClientTreeshakeConfig, getOutputFileNames, withBuildBundlerOptions } from "./build/client-build-config.js";
94
96
  import { createRouteClientReferencesFallbackPlugin, createRscClientReferenceLoadersPlugin } from "./plugins/rsc-client-reference-loaders.js";
@@ -133,7 +135,6 @@ import { createExtensionlessDynamicImportPlugin } from "./plugins/extensionless-
133
135
  import { createWasmModuleImportPlugin } from "./plugins/wasm-module-import.js";
134
136
  import { consumerEnvironmentConditionFilter, getTypeofWindowReplacement, mayFoldChangeScannedImports, replaceConsumerEnvironmentConditions } from "./plugins/typeof-window.js";
135
137
  import { hasMdxFiles, loadMdxEsmReader, pageExtensionsIncludeMdx } from "./utils/mdx-scan.js";
136
- import { publicFileRouteKey } from "./utils/public-file-path.js";
137
138
  import { scanPublicFileRoutes, scanPublicFiles } from "./utils/public-routes.js";
138
139
  import { createIgnoreDynamicRequestsPlugin } from "./plugins/ignore-dynamic-requests.js";
139
140
  import { assertPluginRscVersion, needsAppReactServerDom } from "./utils/plugin-rsc-version.js";
@@ -843,6 +844,7 @@ const PAGES_REQUEST_STAGE_ENTRY = resolveRuntimeEntryModule("pages-request-stage
843
844
  const PAGES_RESPONSE_STAGE_ENTRY = resolveRuntimeEntryModule("pages-response-stage-entry");
844
845
  const NITRO_PUBLIC_FILES_ENTRY = resolveRuntimeEntryModule("nitro-public-files");
845
846
  const NITRO_PUBLIC_FILES_PLUGIN = resolveRuntimeEntryModule("nitro-public-files-plugin");
847
+ const SRVX_MIDDLEWARE_ORIGIN_ENTRY = resolveRuntimeEntryModule("srvx-middleware-origin");
846
848
  const WORKER_ROUTER_ENTRIES = /* @__PURE__ */ new Set([resolveRuntimeEntryModule("app-router-entry"), resolveRuntimeEntryModule("pages-router-entry")]);
847
849
  /** Virtual module that registers config-driven cache adapters (see VinextOptions.cache). */
848
850
  const RESOLVED_CACHE_ADAPTERS = "\0" + VIRTUAL_CACHE_ADAPTERS;
@@ -1466,6 +1468,7 @@ function vinext(options = {}) {
1466
1468
  }),
1467
1469
  dataUrlCssPlugin(),
1468
1470
  createCssModuleImportCompatibilityPlugin(),
1471
+ createCssTargetPlugin(),
1469
1472
  createDevServerLifecyclePlugin({}, (config) => config[VINEXT_DEV_CLI_LIFECYCLE] === true),
1470
1473
  {
1471
1474
  name: "vinext:config",
@@ -1764,12 +1767,6 @@ function vinext(options = {}) {
1764
1767
  } } : {},
1765
1768
  build: {
1766
1769
  ssrEmitAssets: true,
1767
- cssTarget: [
1768
- "chrome111",
1769
- "edge111",
1770
- "firefox114",
1771
- "safari15"
1772
- ],
1773
1770
  assetsDir: clientAssetsDir,
1774
1771
  ...!isSSR && !hasClientBuildEnvironment ? { assetsInlineLimit: clientAssetsInlineLimit } : {},
1775
1772
  ...withBuildBundlerOptions({
@@ -2363,9 +2360,14 @@ function vinext(options = {}) {
2363
2360
  const entry = hasAppDir ? "vinext/server/app-router-entry" : "vinext/server/pages-router-entry";
2364
2361
  if (!hasAppDir && hasNitroPlugin) return [
2365
2362
  `import pagesWorker from ${JSON.stringify(entry)};`,
2363
+ `import { resolveSrvxMiddlewareOrigin } from ${JSON.stringify(SRVX_MIDDLEWARE_ORIGIN_ENTRY)};`,
2366
2364
  ...nitroPublicFiles ? [`import { withNitroPagesPublicFiles } from ${JSON.stringify(NITRO_PUBLIC_FILES_ENTRY)};`, "const worker = withNitroPagesPublicFiles(pagesWorker);"] : ["const worker = pagesWorker;"],
2367
2365
  "export default { fetch(request, env, ctx) {",
2368
- ` return worker.fetch(request, env, { ...ctx, hostRuntime: ${JSON.stringify(nitroHostRuntime)} });`,
2366
+ " return worker.fetch(request, env, {",
2367
+ " ...ctx,",
2368
+ ` hostRuntime: ${JSON.stringify(nitroHostRuntime)},`,
2369
+ " middlewareOrigin: resolveSrvxMiddlewareOrigin(request),",
2370
+ " });",
2369
2371
  "} };"
2370
2372
  ].join("\n");
2371
2373
  return `export { default } from ${JSON.stringify(entry)};`;
@@ -2453,10 +2455,13 @@ function vinext(options = {}) {
2453
2455
  deviceSizes: nextConfig?.images?.deviceSizes,
2454
2456
  imageSizes: nextConfig?.images?.imageSizes,
2455
2457
  qualities: nextConfig?.images?.qualities,
2458
+ localPatterns: nextConfig?.images?.localPatterns,
2456
2459
  dangerouslyAllowSVG: nextConfig?.images?.dangerouslyAllowSVG,
2457
2460
  dangerouslyAllowLocalIP: nextConfig?.images?.dangerouslyAllowLocalIP,
2458
2461
  contentDispositionType: nextConfig?.images?.contentDispositionType,
2459
- contentSecurityPolicy: nextConfig?.images?.contentSecurityPolicy
2462
+ contentSecurityPolicy: nextConfig?.images?.contentSecurityPolicy,
2463
+ minimumCacheTTL: nextConfig?.images?.minimumCacheTTL,
2464
+ maximumResponseBody: nextConfig?.images?.maximumResponseBody
2460
2465
  },
2461
2466
  hasPagesDir,
2462
2467
  publicFiles: isServeCommand && devPublicFileRoutes ? [...devPublicFileRoutes].sort() : scanPublicFileRoutes(root, this.environment.config.publicDir === "" ? false : this.environment.config.publicDir),
@@ -2737,9 +2742,11 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
2737
2742
  handler(_options, bundle) {
2738
2743
  if (this.environment?.name !== "client") return;
2739
2744
  const manifest = {};
2745
+ const appBrowserRuntimeModule = toSlash(resolveRuntimeEntryModule("app-browser-entry"));
2740
2746
  for (const chunk of Object.values(bundle)) {
2741
2747
  if (chunk.type !== "chunk") continue;
2742
2748
  if (chunk.isDynamicEntry && isVirtualEntryFacade(chunk.facadeModuleId, VIRTUAL_APP_ROUTE_MANIFEST)) manifest.appRouteManifest = chunk.fileName;
2749
+ if (chunk.moduleIds.some((id) => toSlash(id) === appBrowserRuntimeModule)) manifest.appBrowserRuntime = chunk.fileName;
2743
2750
  if (!chunk.isEntry) continue;
2744
2751
  if (isVirtualEntryFacade(chunk.facadeModuleId, VIRTUAL_CLIENT_ENTRY)) manifest.pagesClientEntry = chunk.fileName;
2745
2752
  else if (isVirtualEntryFacade(chunk.facadeModuleId, VIRTUAL_APP_BROWSER_ENTRY)) manifest.appBrowserEntry = chunk.fileName;
@@ -3378,14 +3385,17 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
3378
3385
  const viteBase = server.config.base;
3379
3386
  const viteBasePath = viteBase.startsWith("/") && viteBase !== "/" ? viteBase.replace(/\/+$/, "") : "";
3380
3387
  const routingBasePath = bp || viteBasePath;
3388
+ const viteStrippedBasePath = originalRequestUrl !== originalEncodedUrl;
3389
+ let originalUrlHadBasePath = false;
3381
3390
  if (routingBasePath) {
3382
- if (hasBasePath(pathname, routingBasePath)) {
3391
+ if (!viteStrippedBasePath && hasBasePath(pathname, routingBasePath)) {
3383
3392
  const stripped = stripBasePath(pathname, routingBasePath);
3384
3393
  url = stripped + (url.includes("?") ? url.slice(url.indexOf("?")) : "");
3385
3394
  pathname = stripped;
3386
3395
  }
3387
3396
  const middlewarePathname = middlewareUrl.split("?")[0];
3388
3397
  if (hasBasePath(middlewarePathname, routingBasePath)) {
3398
+ originalUrlHadBasePath = true;
3389
3399
  const middlewareQs = middlewareUrl.includes("?") ? middlewareUrl.slice(middlewareUrl.indexOf("?")) : "";
3390
3400
  middlewareUrl = stripBasePath(middlewarePathname, routingBasePath) + middlewareQs;
3391
3401
  }
@@ -3395,7 +3405,7 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
3395
3405
  routeUrl = stripBasePath(routePathname, routingBasePath) + routeQs;
3396
3406
  }
3397
3407
  }
3398
- let configMatchPathname = stripBasePath(middlewareUrl.split("?")[0], routingBasePath);
3408
+ let configMatchPathname = middlewareUrl.split("?")[0];
3399
3409
  if (nextConfig && !nextConfig.skipTrailingSlashRedirect) {
3400
3410
  const qs = url.includes("?") ? url.slice(url.indexOf("?")) : "";
3401
3411
  const trailingSlashRedirect = normalizeTrailingSlash(routeUrl.split("?")[0], bp, nextConfig.trailingSlash, qs);
@@ -3412,7 +3422,7 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
3412
3422
  const rawRoutePathname = routeUrl.split("?")[0];
3413
3423
  if (isNextDataPathname(rawRoutePathname)) {
3414
3424
  const rawDataMatch = parseNextDataPathname(rawRoutePathname, devBuildId);
3415
- const dataMatch = rawDataMatch ? parseNextDataPathname(pathname, devBuildId) : null;
3425
+ const dataMatch = rawDataMatch ? parseNextDataPathname(pathname, devBuildId, true) : null;
3416
3426
  if (dataMatch && rawDataMatch) {
3417
3427
  isDataReq = true;
3418
3428
  const qs = url.includes("?") ? url.slice(url.indexOf("?")) : "";
@@ -3441,25 +3451,6 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
3441
3451
  });
3442
3452
  }
3443
3453
  }
3444
- const filePathMatchesRewrite = [
3445
- ...nextConfig?.rewrites.beforeFiles ?? [],
3446
- ...nextConfig?.rewrites.afterFiles ?? [],
3447
- ...nextConfig?.rewrites.fallback ?? []
3448
- ].some((rewrite) => matchesRewriteSource(pathname, rewrite, {
3449
- basePath: bp,
3450
- hadBasePath: true
3451
- }));
3452
- const isFilePathRequest = pathname.includes(".") && !pathname.endsWith(".html");
3453
- const isExistingPublicMutation = req.method !== "GET" && req.method !== "HEAD" && isExistingDevPublicFile(pathname);
3454
- let filePathMatchesPagesRoute = false;
3455
- const requestHostname = getUrlHostname(requestOrigin);
3456
- if (isFilePathRequest && !filePathMatchesRewrite && !isDataReq) {
3457
- const [pageRoutes, apiRoutes] = await Promise.all([pagesRouter(pagesDir, nextConfig?.pageExtensions, fileMatcher), apiRouter(pagesDir, nextConfig?.pageExtensions, fileMatcher)]);
3458
- const pageRouteUrl = nextConfig?.i18n ? resolvePagesI18nRequest(pathname, nextConfig.i18n, req.headers, requestHostname, bp, nextConfig.trailingSlash ?? false).url : pathname;
3459
- const apiRouteUrl = stripI18nLocaleForApiRoute(pathname, nextConfig?.i18n ?? null);
3460
- filePathMatchesPagesRoute = matchRoute(pageRouteUrl, pageRoutes) !== null || matchRoute(apiRouteUrl, apiRoutes) !== null;
3461
- }
3462
- if (isFilePathRequest && !isDataReq && !filePathMatchesRewrite && !filePathMatchesPagesRoute && !isNextStaticPath(pathname, "", assetPrefixPathname(nextConfig?.assetPrefix ?? "")) && !isExistingPublicMutation) return next();
3463
3454
  const rawHeaders = new Headers(Object.fromEntries(Object.entries(req.headers).filter(([k, v]) => v !== void 0 && !k.startsWith(":")).map(([k, v]) => [k, Array.isArray(v) ? v.join(", ") : String(v)])));
3464
3455
  const isDataRequest = isDataReq;
3465
3456
  const nodeRequestHeaders = filterInternalHeaders(rawHeaders);
@@ -3482,12 +3473,14 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
3482
3473
  const devRunMiddlewareAdapter = capturedMiddlewarePath ? async (_request, _ctx, opts) => {
3483
3474
  const rawProto = process.env.VINEXT_TRUST_PROXY === "1" || (process.env.VINEXT_TRUSTED_HOSTS ?? "").split(",").some((h) => h.trim()) ? String(req.headers["x-forwarded-proto"] || "").split(",")[0].trim() : "";
3484
3475
  const mwOrigin = `${rawProto === "https" || rawProto === "http" ? rawProto : "http"}://${requestHost}`;
3485
- const middlewareRequest = new Request(new URL(middlewareUrl, mwOrigin), {
3476
+ const middlewareRequestUrl = new URL(middlewareUrl, mwOrigin);
3477
+ if (originalUrlHadBasePath) middlewareRequestUrl.pathname = restoreBasePath(middlewareRequestUrl.pathname, bp);
3478
+ const middlewareRequest = new Request(middlewareRequestUrl, {
3486
3479
  method: req.method,
3487
3480
  headers: nodeRequestHeaders,
3488
3481
  signal: webRequest.signal
3489
3482
  });
3490
- const result = await runMiddleware(getPagesRunner(), capturedMiddlewarePath, middlewareRequest, nextConfig?.i18n, nextConfig?.basePath, nextConfig?.trailingSlash, opts.isDataRequest, pathname, opts.isStaleDataRequest, nextConfig?.skipTrailingSlashRedirect);
3483
+ const result = await runMiddleware(getPagesRunner(), capturedMiddlewarePath, middlewareRequest, nextConfig?.i18n, nextConfig?.basePath, nextConfig?.trailingSlash, opts.isDataRequest, pathname, opts.isStaleDataRequest, nextConfig?.skipTrailingSlashRedirect, opts.restoreLocaleRootTrailingSlash);
3491
3484
  if (hasAppDir && result.continue) {
3492
3485
  const mwCtxEntries = [];
3493
3486
  if (result.responseHeaders) {
@@ -3628,7 +3621,8 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
3628
3621
  trustedRevalidateOrigin: getTrustedDevRevalidateOrigin(),
3629
3622
  allowedRevalidateHeaderKeys: nextConfig?.allowedRevalidateHeaderKeys,
3630
3623
  trailingSlash: nextConfig?.trailingSlash,
3631
- skipTrailingSlashRedirect: nextConfig?.skipTrailingSlashRedirect
3624
+ skipTrailingSlashRedirect: nextConfig?.skipTrailingSlashRedirect,
3625
+ requestPathname: originalUrlHadBasePath ? restoreBasePath(routeUrl.split("?")[0], bp) : routeUrl.split("?")[0]
3632
3626
  })) return;
3633
3627
  if (hasAppDir) {
3634
3628
  if (pipelineResult.configRewriteFired) forwardInternalApiRewriteToApp(pipelineResult.apiUrl);
@@ -4117,6 +4111,11 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
4117
4111
  nitroHostRuntime = nitro.options.exportConditions?.includes("workerd") ? "worker" : "node";
4118
4112
  if (!nextConfig) return;
4119
4113
  if (!hasAppDir && !hasPagesDir) return;
4114
+ if (!nitro.options.dev) {
4115
+ const requestTargetPlugin = resolveRuntimeEntryModule("nitro-request-target-plugin");
4116
+ nitro.options.plugins ??= [];
4117
+ if (!nitro.options.plugins.includes(requestTargetPlugin)) nitro.options.plugins.push(requestTargetPlugin);
4118
+ }
4120
4119
  nitroPublicFiles = registerNitroPublicFiles(nitro.options, NITRO_PUBLIC_FILES_PLUGIN, () => scanPublicFiles(root, nitroPublicDir));
4121
4120
  if (resolvedServerExternalPackages.length > 0) nitro.options.traceDeps = [.../* @__PURE__ */ new Set([...nitro.options.traceDeps ?? [], ...resolvedServerExternalPackages])];
4122
4121
  } }
@@ -4263,6 +4262,7 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
4263
4262
  pagesClientAssetsModule = buildPagesClientAssetsModule({
4264
4263
  clientEntry: runtimeMetadata.clientEntryFile ?? void 0,
4265
4264
  appBootstrapPreinitModules: runtimeMetadata.appBootstrapPreinitModules,
4265
+ appBootstrapPreloadModules: runtimeMetadata.appBootstrapPreloadModules,
4266
4266
  appRouteManifestModule: runtimeMetadata.appRouteManifestModule,
4267
4267
  appClientReferenceChunks: runtimeMetadata.appClientReferenceChunks,
4268
4268
  ssrManifest,
@@ -140,7 +140,7 @@ function setupCloudflarePlatform(context, cloudflare) {
140
140
  generatedPlatformFiles.push(path.relative(context.root, responseStorePath) || RESPONSE_STORE_WRANGLER_CONFIG);
141
141
  }
142
142
  }
143
- const nextSteps = [];
143
+ const nextSteps = !wranglerCode || !findTopLevelJsonProperty(wranglerCode, "main") ? workerEntryWarning(context.root, context.isStaticExport) : [];
144
144
  if (cloudflare.cdnCache === "response-store" && (cloudflare.responseStoreMode ?? "service-binding") === "service-binding") nextSteps.push("Deploy Workers Response Store before deploying the application:", ` ${context.packageManager ?? "npm"} run deploy:response-store`);
145
145
  if (cloudflare.prerender && cloudflare.cdnCache !== "static-assets") nextSteps.push("Pre-rendered routes are built, but Cloudflare deploys do not serve them.", " Use the Static Assets cache to serve them, or cache warming to fill another cache.");
146
146
  return {
@@ -193,14 +193,15 @@ function setupCfPlatform(context, cloudflare) {
193
193
  }
194
194
  const configPath = path.join(context.root, "cloudflare.config.ts");
195
195
  const generatedPlatformFiles = [];
196
+ const nextSteps = [];
196
197
  if (!fs.existsSync(configPath)) {
197
198
  fs.writeFileSync(configPath, generateTypedCloudflareConfig({
198
199
  ...projectInfo,
199
200
  isStaticExport: context.isStaticExport
200
201
  }, cloudflare, context.today));
201
202
  generatedPlatformFiles.push("cloudflare.config.ts");
203
+ nextSteps.push(...workerEntryWarning(context.root, context.isStaticExport));
202
204
  }
203
- const nextSteps = [];
204
205
  if (serviceBinding) nextSteps.push("After `vinext build`, deploy the Response Store Worker explicitly (and repeat when its code/config changes):", ` ${context.packageManager ?? "npm"} run deploy:response-store`, "vinext-cloudflare deploy only deploys the application Worker.");
205
206
  if (cloudflare.prerender && cloudflare.cdnCache !== "static-assets") nextSteps.push("Pre-rendered routes are built, but Cloudflare deploys do not serve them.", " Use the Static Assets cache to serve them, or cache warming to fill another cache.");
206
207
  nextSteps.push("For TypeScript, add \".cloudflare/types\" to the include list in tsconfig.json.", "Worker types are generated during dev/build; run `cf workers types` before standalone type-checks.");
@@ -280,6 +281,21 @@ function resolveWorkerEntry(root) {
280
281
  return "vinext/server/fetch-handler";
281
282
  }
282
283
  /**
284
+ * A `worker/index.*` that never reaches vinext's handler (say, a migrated Vite
285
+ * SPA's API Worker) answers every page route itself, so each one 404s (#4460).
286
+ */
287
+ function workerEntryWarning(root, isStaticExport) {
288
+ if (isStaticExport) return [];
289
+ const entry = resolveWorkerEntry(root);
290
+ if (entry === "vinext/server/fetch-handler") return [];
291
+ if (fs.readFileSync(path.join(root, entry), "utf-8").includes("vinext/server/fetch-handler")) return [];
292
+ return [
293
+ `Warning: ${entry.slice(2)} does not import vinext/server/fetch-handler. As the Worker entry`,
294
+ " it must hand requests to vinext, or every page 404s. Otherwise point the Worker entry",
295
+ " (entrypoint in cloudflare.config.ts, or main in wrangler.jsonc) at vinext/server/fetch-handler."
296
+ ];
297
+ }
298
+ /**
283
299
  * Next.js runs no middleware for a static export, and an export with
284
300
  * `trailingSlash` relies on the default html_handling to serve `/about/` from
285
301
  * `about/index.html`, so export apps keep asset-first serving.
@@ -148,9 +148,9 @@ async function resolveInitOptions(args, options = {}) {
148
148
  if (name && platform !== "cloudflare") throw new Error("--name requires --platform=cloudflare.");
149
149
  const explicitPrerender = parsePrerenderArg(args);
150
150
  if (platform === "cloudflare" && platformOptions?.cdnCache === "static-assets" && explicitPrerender === false) throw new Error("--no-prerender cannot be used with --cdn-cache=static-assets, which requires build-time prerendering.");
151
- const supportsWarmCdnCache = platformOptions?.cdnCache === "response-store" || platformOptions?.cdnCache === "workers-cache";
151
+ const supportsWarmCdnCache = platformOptions?.cdnCache !== void 0 && platformOptions.cdnCache !== "none";
152
152
  if (platform === "cloudflare" && !supportsWarmCdnCache) {
153
- if (explicitWarmCdnCache === true) throw new Error("--warm-cache requires --cdn-cache=response-store or workers-cache.");
153
+ if (explicitWarmCdnCache === true) throw new Error("--warm-cache requires a cache: pass --cdn-cache=response-store, workers-cache, data-cache, or static-assets.");
154
154
  }
155
155
  const prerender = platform === "cloudflare" && platformOptions?.cdnCache === "static-assets" ? false : explicitPrerender ?? await resolveInitPrerender(args, options, platform);
156
156
  const warmCdnCache = platform === "cloudflare" && supportsWarmCdnCache ? await resolveInitWarmCdnCache(args, options) : false;
package/dist/init.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import path, { toSlash } from "./deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
2
  import { detectPackageManager, detectPackageManagerName, detectProject, ensureESModule, findViteConfigPath, hasViteConfig, renameCJSConfigs } from "./utils/project.js";
3
3
  import { compactResourceName, cssModulesConfigSource, findConfigKeepingWorkerName, setupCloudflarePlatform, updateViteConfigForCssModules, usesCommonJsViteConfig, validateCloudflarePlatformSetup } from "./init-cloudflare.js";
4
- import { getReactUpgradeDeps } from "./utils/react-version.js";
4
+ import { findPackageVersion, getReactUpgradeDeps } from "./utils/react-version.js";
5
5
  import { getPluginReactUpgradeDeps } from "./utils/plugin-react-version.js";
6
+ import { createRequire } from "node:module";
6
7
  import fs from "node:fs";
7
8
  import { spawn, spawnSync } from "node:child_process";
8
9
  //#region src/init.ts
@@ -297,6 +298,26 @@ function addDependencyEntries(root, deps, { dev }) {
297
298
  if (added.length > 0) fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
298
299
  return added;
299
300
  }
301
+ /** The spec in the manifest sections `isDepInstalled` reads. */
302
+ function readDeclaredDependency(root, name) {
303
+ try {
304
+ const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf-8"));
305
+ return pkg.dependencies?.[name] ?? pkg.devDependencies?.[name] ?? pkg.peerDependencies?.[name];
306
+ } catch {
307
+ return;
308
+ }
309
+ }
310
+ /** The declared spec, or the installed version of a package hoisted from elsewhere. */
311
+ function readCurrentDependencyVersion(root, name) {
312
+ const declared = readDeclaredDependency(root, name);
313
+ if (declared !== void 0) return declared;
314
+ try {
315
+ const resolved = createRequire(path.join(root, "package.json")).resolve(name);
316
+ return findPackageVersion(resolved, name) ?? void 0;
317
+ } catch {
318
+ return;
319
+ }
320
+ }
300
321
  /**
301
322
  * Check if react/react-dom need upgrading for react-server-dom-webpack compatibility.
302
323
  *
@@ -578,12 +599,20 @@ async function init(options) {
578
599
  let dependencyInstallNeedsApproval = false;
579
600
  const dependencyEntriesAdded = [];
580
601
  const devDependencyEntriesAdded = [];
602
+ const upgradedFrom = /* @__PURE__ */ new Map();
603
+ const recordUpgrades = (deps) => {
604
+ for (const dep of deps) {
605
+ const { name } = parseDependencySpecifier(dep);
606
+ upgradedFrom.set(name, readCurrentDependencyVersion(root, name));
607
+ }
608
+ };
581
609
  if (isApp) {
582
610
  const reactUpgrade = shouldInstall ? [.../* @__PURE__ */ new Set([...getReactUpgradeDeps(root), ...getReactUpgradeDeps(root, {
583
611
  fromManifest: true,
584
612
  installing: true
585
613
  })])] : getReactUpgradeDeps(root, { fromManifest: true });
586
614
  if (reactUpgrade.length > 0) {
615
+ recordUpgrades(reactUpgrade);
587
616
  console.log(` ${terminalStyle.cyan(terminalStyle.bold("Upgrading dependencies:"))}`);
588
617
  console.log(formatList(reactUpgrade.map((dep) => dep.replace(/@latest$/, "")), " "));
589
618
  try {
@@ -601,6 +630,7 @@ async function init(options) {
601
630
  }
602
631
  const pluginReactUpgrade = getPluginReactUpgradeDeps(root);
603
632
  if (pluginReactUpgrade.length > 0) {
633
+ recordUpgrades(pluginReactUpgrade);
604
634
  console.log(` ${terminalStyle.cyan(terminalStyle.bold("Upgrading devDependencies:"))}`);
605
635
  console.log(formatList(pluginReactUpgrade.map((dep) => dep.replace(/@latest$/, "")), " "));
606
636
  try {
@@ -654,13 +684,20 @@ async function init(options) {
654
684
  if (pmName === "pnpm" && shouldInstall && !dependencyInstallNeedsApproval && !options._exec && hasAutomaticallyIgnoredBuilds(inspectPnpmIgnoredBuilds(root))) dependencyInstallNeedsApproval = true;
655
685
  else if (pmName === "pnpm" && shouldInstall && !dependencyInstallNeedsApproval && options._inspectPnpmIgnoredBuilds && hasAutomaticallyIgnoredBuilds(options._inspectPnpmIgnoredBuilds(root))) dependencyInstallNeedsApproval = true;
656
686
  console.log(` ${terminalStyle.green(terminalStyle.bold("vinext init complete!"))}\n`);
657
- if (dependencyEntriesAdded.length > 0) {
687
+ const addedDependencies = dependencyEntriesAdded.filter((name) => !upgradedFrom.has(name));
688
+ const addedDevDependencies = devDependencyEntriesAdded.filter((name) => !upgradedFrom.has(name));
689
+ if (addedDependencies.length > 0) {
658
690
  console.log(` ${terminalStyle.green("✓")} Added dependencies to dependencies:`);
659
- console.log(formatList(dependencyEntriesAdded, " "));
691
+ console.log(formatList(addedDependencies, " "));
660
692
  }
661
- if (devDependencyEntriesAdded.length > 0) {
693
+ if (addedDevDependencies.length > 0) {
662
694
  console.log(` ${terminalStyle.green("✓")} Added dependencies to devDependencies:`);
663
- console.log(formatList(devDependencyEntriesAdded, " "));
695
+ console.log(formatList(addedDevDependencies, " "));
696
+ }
697
+ for (const [name, from] of upgradedFrom) {
698
+ const to = readDeclaredDependency(root, name);
699
+ if (to === void 0 || to === from) continue;
700
+ console.log(` ${terminalStyle.green("✓")} Upgraded ${name} ${from ?? "(not installed)"} \u2192 ${to}`);
664
701
  }
665
702
  if (dependencyInstallNeedsApproval) console.log(` ${terminalStyle.yellow("!")} Dependency installation is waiting for build-script approval`);
666
703
  if (addedTypeModule) console.log(` ${terminalStyle.green("✓")} Added "type": "module" to package.json`);
@@ -0,0 +1,18 @@
1
+ import { Plugin } from "vite";
2
+ //#region src/plugins/css-target.d.ts
3
+ /**
4
+ * Applies {@link DEFAULT_CSS_TARGET} only where nobody chose a target (#4413).
5
+ *
6
+ * Vite merges config by concatenating arrays, and the CSS minifier keeps the
7
+ * oldest version of each browser, so a pin merged onto a chosen target would
8
+ * stop an app from ever raising it. Instead nothing is set while config hooks
9
+ * run, so the app and every plugin hook see and merge plain Vite values. The
10
+ * post-ordered `config` and `configEnvironment` hooks only note where a target
11
+ * was set, and the pin is applied once config is resolved.
12
+ *
13
+ * Both the top-level `build`, which Vite's CSS plugin minifies with (a copy of
14
+ * the environment's in per-environment builds), and every environment's `build`
15
+ * are checked.
16
+ */
17
+ export declare function createCssTargetPlugin(): Plugin;
18
+ //#endregion
@@ -0,0 +1,93 @@
1
+ //#region src/plugins/css-target.ts
2
+ /**
3
+ * CSS minification target. Vite defaults it to the same target as JS
4
+ * (modern evergreens), which lets the CSS minifier (Lightning CSS by
5
+ * default, or esbuild) rewrite
6
+ * `@media (max-width: 768px)` to the Media Queries Level 4 range
7
+ * syntax `@media (width <= 768px)`. Both forms are semantically
8
+ * equivalent in modern browsers, but the rewrite is observable to
9
+ * user code that inspects `cssText` of `CSSMediaRule`s and breaks
10
+ * tools that pattern-match the raw query string. Next.js does not
11
+ * perform this rewrite by default (its webpack/lightningcss CSS
12
+ * pipeline preserves the original syntax), so user code carried
13
+ * over from Next.js can break when migrating to vinext.
14
+ *
15
+ * The minifier lowers a CSS feature when ANY target in the list lacks
16
+ * support, so we only need to pin one engine below the range-syntax
17
+ * baseline. Range syntax shipped in Chrome 104, Edge 104, Firefox 63,
18
+ * and Safari 16.4 (per esbuild's `internal/compat/css_table.go`
19
+ * MediaRange entry — and caniuse). Of those, Safari is the latest:
20
+ * pinning `safari15` (semantically Safari 15.0, which predates
21
+ * Safari 16.4) is sufficient to suppress the rewrite on its own.
22
+ *
23
+ * The other three targets are pinned to ~2023 baselines instead of
24
+ * the absolute oldest supported version so the minifier does NOT
25
+ * collaterally downlevel unrelated modern CSS features. With
26
+ * chrome111/edge111/firefox114 we keep through:
27
+ * - `:is()` pseudo-class (Chrome 88, Firefox 78)
28
+ * - `hwb()` colors (Chrome 101, Firefox 96)
29
+ * - `lab()`, `oklch()`, `color()` (Chrome 111, Firefox 113)
30
+ * - gradient interpolation hints (Chrome 111)
31
+ * CSS Nesting (Chrome 120, Safari 17.2) and Firefox-137 gradient
32
+ * interpolation will still be lowered; that is an intentional
33
+ * trade-off — those features are newer than the baseline and
34
+ * lowering them is the correct behavior for our target audience.
35
+ *
36
+ * Mirrors the Next.js fixture
37
+ * test/e2e/app-dir/css-media-query/css-media-query.test.ts which
38
+ * asserts `cssText` preserves `max-width: 768px`.
39
+ */
40
+ const DEFAULT_CSS_TARGET = [
41
+ "chrome111",
42
+ "edge111",
43
+ "firefox114",
44
+ "safari15"
45
+ ];
46
+ /**
47
+ * Applies {@link DEFAULT_CSS_TARGET} only where nobody chose a target (#4413).
48
+ *
49
+ * Vite merges config by concatenating arrays, and the CSS minifier keeps the
50
+ * oldest version of each browser, so a pin merged onto a chosen target would
51
+ * stop an app from ever raising it. Instead nothing is set while config hooks
52
+ * run, so the app and every plugin hook see and merge plain Vite values. The
53
+ * post-ordered `config` and `configEnvironment` hooks only note where a target
54
+ * was set, and the pin is applied once config is resolved.
55
+ *
56
+ * Both the top-level `build`, which Vite's CSS plugin minifies with (a copy of
57
+ * the environment's in per-environment builds), and every environment's `build`
58
+ * are checked.
59
+ */
60
+ function createCssTargetPlugin() {
61
+ let topLevelConfigured = false;
62
+ const configuredEnvironments = /* @__PURE__ */ new Set();
63
+ return {
64
+ name: "vinext:css-target",
65
+ enforce: "post",
66
+ config: {
67
+ order: "post",
68
+ handler(config) {
69
+ topLevelConfigured = config.build?.cssTarget !== void 0;
70
+ configuredEnvironments.clear();
71
+ }
72
+ },
73
+ configEnvironment: {
74
+ order: "post",
75
+ handler(name, config) {
76
+ if (config.build?.cssTarget !== void 0) configuredEnvironments.add(name);
77
+ }
78
+ },
79
+ configResolved: {
80
+ order: "pre",
81
+ handler(config) {
82
+ pinUnsetCssTarget(config.build, topLevelConfigured);
83
+ for (const [name, environment] of Object.entries(config.environments)) pinUnsetCssTarget(environment.build, configuredEnvironments.has(name));
84
+ }
85
+ }
86
+ };
87
+ }
88
+ function pinUnsetCssTarget(build, configured) {
89
+ if (configured || build.cssTarget !== build.target) return;
90
+ build.cssTarget = [...DEFAULT_CSS_TARGET];
91
+ }
92
+ //#endregion
93
+ export { createCssTargetPlugin };
@@ -109,6 +109,10 @@ type ParallelSlot = {
109
109
  loadingTreePositions?: number[];
110
110
  /** Absolute path to the slot's error component */
111
111
  errorPath: string | null;
112
+ /** Every error convention from the slot root to its active page, outermost first. */
113
+ errorPaths?: string[];
114
+ /** Slot-root-relative tree positions aligned with errorPaths. */
115
+ errorTreePositions?: number[];
112
116
  /** Nearest not-found convention for the slot's active branch. */
113
117
  notFoundPath?: string | null;
114
118
  /** Tree position of that convention relative to the slot root. */
@@ -167,6 +171,12 @@ export type AppRoute = {
167
171
  id: string;
168
172
  ownerTreePath: string;
169
173
  state: "active" | "default" | "unmatched";
174
+ /**
175
+ * Set on a sub-route minted for a slot page with no children page of its
176
+ * own: its children render a default, a sibling catch-all, or nothing, so
177
+ * its route segments don't describe them.
178
+ */
179
+ synthetic?: true;
170
180
  };
171
181
  /**
172
182
  * Interception markers not wrapped in an `@slot` directory.
@@ -594,7 +594,8 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
594
594
  childrenSlot: {
595
595
  id: childrenSlotId,
596
596
  ownerTreePath: childrenOwnerTreePath,
597
- state: childrenDefault ? "default" : childrenCatchAll ? "active" : "unmatched"
597
+ state: childrenDefault ? "default" : childrenCatchAll ? "active" : "unmatched",
598
+ synthetic: true
598
599
  },
599
600
  loadingPath: null,
600
601
  loadingPaths: parentRoute.loadingPaths,
@@ -742,10 +743,22 @@ function withSlotLoadingEntries(slot, pagePath, matcher) {
742
743
  ...slot,
743
744
  loadingPaths: loadingEntries.map((entry) => entry.path),
744
745
  loadingTreePositions: loadingEntries.map((entry) => entry.treePosition),
746
+ ...findSlotErrorFields(slot.ownerDir, pagePath, matcher),
745
747
  ...findSlotNotFoundFields(slot.ownerDir, pagePath, matcher)
746
748
  };
747
749
  }
748
750
  /**
751
+ * Every error convention on the slot's active branch. Each catches errors in
752
+ * its own segment, below that segment's layout.
753
+ */
754
+ function findSlotErrorFields(slotDir, pagePath, matcher) {
755
+ const entries = findSlotSegmentFileEntries(slotDir, pagePath, "error", matcher);
756
+ return {
757
+ errorPaths: entries.map((entry) => entry.path),
758
+ errorTreePositions: entries.map((entry) => entry.treePosition)
759
+ };
760
+ }
761
+ /**
749
762
  * Every not-found convention on the slot's active branch, plus the nearest one,
750
763
  * which supplies the slot's not-found metadata.
751
764
  */
@@ -1347,6 +1360,7 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
1347
1360
  loadingPaths: loadingEntries.map((loading) => loading.path),
1348
1361
  loadingTreePositions: loadingEntries.map((loading) => loading.treePosition),
1349
1362
  errorPath: findFile(slotDir, "error", matcher),
1363
+ ...findSlotErrorFields(slotDir, pagePath, matcher),
1350
1364
  ...findSlotNotFoundFields(slotDir, pagePath, matcher),
1351
1365
  interceptingRoutes,
1352
1366
  layoutIndex: -1,