vinext 0.1.3 → 0.1.5

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 (185) hide show
  1. package/dist/build/client-build-config.d.ts +11 -2
  2. package/dist/build/client-build-config.js +17 -6
  3. package/dist/build/css-url-assets.d.ts +1 -1
  4. package/dist/build/css-url-assets.js +9 -7
  5. package/dist/build/prerender.js +3 -1
  6. package/dist/cache/cache-adapters-virtual.js +1 -1
  7. package/dist/client/pages-router-link-navigation.d.ts +33 -7
  8. package/dist/client/pages-router-link-navigation.js +32 -2
  9. package/dist/client/vinext-next-data.js +2 -0
  10. package/dist/cloudflare/src/cache/kv-data-adapter.runtime.d.ts +1 -1
  11. package/dist/config/config-matchers.d.ts +11 -1
  12. package/dist/config/config-matchers.js +14 -2
  13. package/dist/config/tsconfig-paths.js +14 -1
  14. package/dist/deploy.js +20 -13
  15. package/dist/entries/app-rsc-entry.js +27 -22
  16. package/dist/entries/pages-client-entry.js +14 -13
  17. package/dist/entries/pages-server-entry.js +8 -27
  18. package/dist/index.js +365 -147
  19. package/dist/plugins/css-data-url.js +30 -26
  20. package/dist/plugins/dynamic-preload-metadata.js +2 -4
  21. package/dist/plugins/extensionless-dynamic-import.js +27 -24
  22. package/dist/plugins/fonts.js +5 -4
  23. package/dist/plugins/import-meta-url.js +21 -15
  24. package/dist/plugins/instrumentation-client.js +1 -1
  25. package/dist/plugins/middleware-server-only.js +7 -6
  26. package/dist/plugins/og-assets.js +48 -46
  27. package/dist/plugins/optimize-imports.js +9 -3
  28. package/dist/plugins/remove-console.d.ts +7 -1
  29. package/dist/plugins/remove-console.js +4 -1
  30. package/dist/plugins/require-context.js +21 -20
  31. package/dist/plugins/strip-server-exports.d.ts +16 -8
  32. package/dist/plugins/strip-server-exports.js +496 -46
  33. package/dist/routing/app-route-graph.js +2 -2
  34. package/dist/server/app-bfcache-identity.d.ts +26 -0
  35. package/dist/server/app-bfcache-identity.js +127 -0
  36. package/dist/server/app-browser-action-result.js +1 -1
  37. package/dist/server/app-browser-entry.js +22 -12
  38. package/dist/server/app-browser-navigation-controller.d.ts +1 -1
  39. package/dist/server/app-browser-navigation-controller.js +1 -1
  40. package/dist/server/app-browser-state.d.ts +3 -22
  41. package/dist/server/app-browser-state.js +23 -139
  42. package/dist/server/app-browser-stream.js +1 -1
  43. package/dist/server/app-browser-visible-commit.d.ts +1 -1
  44. package/dist/server/app-browser-visible-commit.js +3 -2
  45. package/dist/server/app-fallback-renderer.d.ts +1 -1
  46. package/dist/server/app-layout-param-observation.d.ts +1 -1
  47. package/dist/server/app-layout-param-observation.js +1 -1
  48. package/dist/server/app-middleware.js +2 -1
  49. package/dist/server/app-page-boundary-render.d.ts +1 -1
  50. package/dist/server/app-page-boundary.js +1 -1
  51. package/dist/server/app-page-cache-finalizer.d.ts +62 -0
  52. package/dist/server/app-page-cache-finalizer.js +122 -0
  53. package/dist/server/app-page-cache-render.d.ts +2 -2
  54. package/dist/server/app-page-cache-render.js +1 -1
  55. package/dist/server/app-page-cache.d.ts +2 -53
  56. package/dist/server/app-page-cache.js +5 -131
  57. package/dist/server/app-page-dispatch.d.ts +2 -2
  58. package/dist/server/app-page-dispatch.js +10 -8
  59. package/dist/server/app-page-probe.js +3 -2
  60. package/dist/server/app-page-render-observation.js +2 -2
  61. package/dist/server/app-page-render.d.ts +3 -3
  62. package/dist/server/app-page-render.js +3 -2
  63. package/dist/server/app-page-stream.d.ts +2 -9
  64. package/dist/server/app-page-stream.js +1 -35
  65. package/dist/server/app-pages-bridge.d.ts +5 -1
  66. package/dist/server/app-pages-bridge.js +5 -13
  67. package/dist/server/app-request-context.d.ts +1 -2
  68. package/dist/server/app-request-context.js +2 -1
  69. package/dist/server/app-route-handler-dispatch.js +3 -2
  70. package/dist/server/app-route-handler-execution.d.ts +1 -1
  71. package/dist/server/app-route-handler-execution.js +1 -1
  72. package/dist/server/app-route-handler-response.d.ts +1 -1
  73. package/dist/server/app-router-entry.js +2 -1
  74. package/dist/server/app-rsc-handler.d.ts +3 -0
  75. package/dist/server/app-rsc-handler.js +73 -31
  76. package/dist/server/app-rsc-response-finalizer.js +1 -1
  77. package/dist/server/app-rsc-route-matching.js +6 -2
  78. package/dist/server/app-server-action-execution.d.ts +1 -1
  79. package/dist/server/app-server-action-execution.js +10 -6
  80. package/dist/server/app-ssr-entry.d.ts +1 -1
  81. package/dist/server/app-ssr-entry.js +12 -38
  82. package/dist/server/app-ssr-router-instance.d.ts +6 -0
  83. package/dist/server/app-ssr-router-instance.js +24 -0
  84. package/dist/server/app-ssr-stream.js +1 -1
  85. package/dist/server/artifact-compatibility.js +1 -1
  86. package/dist/server/before-interactive-head.d.ts +17 -0
  87. package/dist/server/before-interactive-head.js +35 -0
  88. package/dist/server/client-reuse-manifest.js +1 -1
  89. package/dist/server/csp.js +1 -4
  90. package/dist/server/defer-until-stream-consumed.d.ts +7 -0
  91. package/dist/server/defer-until-stream-consumed.js +34 -0
  92. package/dist/server/dev-server.js +82 -37
  93. package/dist/server/instrumentation.js +1 -1
  94. package/dist/server/isr-cache.d.ts +1 -1
  95. package/dist/server/isr-cache.js +1 -1
  96. package/dist/server/isr-decision.d.ts +1 -1
  97. package/dist/server/middleware-matcher.js +20 -9
  98. package/dist/server/middleware-runtime.d.ts +3 -4
  99. package/dist/server/middleware-runtime.js +4 -2
  100. package/dist/server/navigation-planner.d.ts +3 -12
  101. package/dist/server/navigation-planner.js +24 -0
  102. package/dist/server/navigation-trace.d.ts +2 -1
  103. package/dist/server/navigation-trace.js +1 -0
  104. package/dist/server/open-redirect.d.ts +12 -0
  105. package/dist/server/open-redirect.js +21 -0
  106. package/dist/server/operation-token.d.ts +40 -0
  107. package/dist/server/operation-token.js +85 -0
  108. package/dist/server/pages-data-route.d.ts +1 -1
  109. package/dist/server/pages-data-route.js +7 -4
  110. package/dist/server/pages-dev-module-url.d.ts +4 -0
  111. package/dist/server/pages-dev-module-url.js +15 -0
  112. package/dist/server/pages-document-initial-props.d.ts +4 -15
  113. package/dist/server/pages-document-initial-props.js +27 -56
  114. package/dist/server/pages-i18n.js +2 -2
  115. package/dist/server/pages-page-data.d.ts +1 -1
  116. package/dist/server/pages-page-data.js +3 -1
  117. package/dist/server/pages-page-handler.js +3 -1
  118. package/dist/server/pages-page-response.d.ts +3 -1
  119. package/dist/server/pages-page-response.js +6 -6
  120. package/dist/server/pages-readiness.js +1 -1
  121. package/dist/server/pages-request-pipeline.d.ts +7 -7
  122. package/dist/server/pages-request-pipeline.js +63 -21
  123. package/dist/server/prod-server.d.ts +3 -1
  124. package/dist/server/prod-server.js +43 -11
  125. package/dist/server/request-pipeline.d.ts +1 -24
  126. package/dist/server/request-pipeline.js +1 -33
  127. package/dist/server/seed-cache.d.ts +1 -1
  128. package/dist/server/static-file-cache.js +16 -4
  129. package/dist/shims/before-interactive-context.d.ts +14 -3
  130. package/dist/shims/cache-handler.d.ts +106 -0
  131. package/dist/shims/cache-handler.js +176 -0
  132. package/dist/shims/cache-request-state.d.ts +47 -0
  133. package/dist/shims/cache-request-state.js +126 -0
  134. package/dist/shims/cache-runtime.d.ts +2 -2
  135. package/dist/shims/cache-runtime.js +3 -14
  136. package/dist/shims/cache.d.ts +3 -231
  137. package/dist/shims/cache.js +17 -383
  138. package/dist/shims/cdn-cache.d.ts +1 -1
  139. package/dist/shims/cdn-cache.js +1 -1
  140. package/dist/shims/document.d.ts +15 -20
  141. package/dist/shims/document.js +5 -8
  142. package/dist/shims/error-boundary-navigation.d.ts +7 -0
  143. package/dist/shims/error-boundary-navigation.js +44 -0
  144. package/dist/shims/error-boundary.js +10 -8
  145. package/dist/shims/error.js +2 -1
  146. package/dist/shims/fetch-cache.js +1 -1
  147. package/dist/shims/form.js +1 -1
  148. package/dist/shims/image.js +74 -9
  149. package/dist/shims/internal/app-page-props-cache-key.d.ts +5 -0
  150. package/dist/shims/internal/app-page-props-cache-key.js +16 -0
  151. package/dist/shims/internal/navigation-untracked.js +2 -1
  152. package/dist/shims/internal/pages-data-fetch-dedup.d.ts +6 -7
  153. package/dist/shims/internal/pages-data-fetch-dedup.js +67 -14
  154. package/dist/shims/internal/pages-data-target.js +1 -1
  155. package/dist/shims/layout-segment-context.d.ts +1 -1
  156. package/dist/shims/layout-segment-context.js +2 -1
  157. package/dist/shims/link.js +38 -17
  158. package/dist/shims/metadata.js +4 -4
  159. package/dist/shims/navigation-context-state.d.ts +40 -0
  160. package/dist/shims/navigation-context-state.js +116 -0
  161. package/dist/shims/navigation-errors.d.ts +55 -0
  162. package/dist/shims/navigation-errors.js +110 -0
  163. package/dist/shims/navigation-server.d.ts +3 -0
  164. package/dist/shims/navigation-server.js +3 -0
  165. package/dist/shims/navigation-state.d.ts +1 -2
  166. package/dist/shims/navigation-state.js +2 -1
  167. package/dist/shims/navigation.d.ts +3 -291
  168. package/dist/shims/navigation.js +16 -445
  169. package/dist/shims/navigation.react-server.d.ts +2 -2
  170. package/dist/shims/navigation.react-server.js +3 -1
  171. package/dist/shims/request-state-types.d.ts +3 -3
  172. package/dist/shims/router.d.ts +6 -2
  173. package/dist/shims/router.js +99 -20
  174. package/dist/shims/script.js +9 -5
  175. package/dist/shims/slot.js +3 -1
  176. package/dist/shims/unified-request-context.d.ts +2 -2
  177. package/dist/utils/has-trailing-comma.d.ts +24 -0
  178. package/dist/utils/has-trailing-comma.js +62 -0
  179. package/dist/utils/text-stream.d.ts +1 -1
  180. package/dist/utils/text-stream.js +2 -2
  181. package/dist/utils/virtual-module.d.ts +5 -0
  182. package/dist/utils/virtual-module.js +0 -0
  183. package/dist/utils/vite-version.d.ts +12 -1
  184. package/dist/utils/vite-version.js +9 -1
  185. package/package.json +5 -1
package/dist/index.js CHANGED
@@ -1,12 +1,14 @@
1
1
  import { detectPackageManager } from "./utils/project.js";
2
2
  import { normalizePathSeparators, stripJsExtension, stripViteModuleQuery } from "./utils/path.js";
3
3
  import { normalizePathnameForRouteMatchStrict } from "./routing/utils.js";
4
+ import { escapeRegExp } from "./utils/regex.js";
4
5
  import { buildViteResolveExtensions, createValidFileMatcher, findFileWithExts, normalizeViteResolveExtensions } from "./routing/file-matcher.js";
5
6
  import { apiRouter, invalidateRouteCache, matchRoute, pagesRouter } from "./routing/pages-router.js";
6
7
  import { INTERNAL_HEADERS, NEXTJS_DEPLOYMENT_ID_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MW_CTX_HEADER, VINEXT_TIMING_HEADER } from "./server/headers.js";
7
8
  import { normalizePath as normalizePath$1 } from "./server/normalize-path.js";
8
- import { proxyExternalRequest } from "./config/config-matchers.js";
9
- import { filterInternalHeaders, isOpenRedirectShaped, normalizeTrailingSlash } from "./server/request-pipeline.js";
9
+ import { matchesRewriteSource, proxyExternalRequest } from "./config/config-matchers.js";
10
+ import { isOpenRedirectShaped } from "./server/open-redirect.js";
11
+ import { filterInternalHeaders, normalizeTrailingSlash } from "./server/request-pipeline.js";
10
12
  import { findMiddlewareFile, runMiddleware } from "./server/middleware.js";
11
13
  import { generateServerEntry } from "./entries/pages-server-entry.js";
12
14
  import { generateClientEntry } from "./entries/pages-client-entry.js";
@@ -31,7 +33,7 @@ import { planRouteClassificationInjection } from "./build/route-classification-i
31
33
  import { PHASE_DEVELOPMENT_SERVER, PHASE_PRODUCTION_BUILD } from "./shims/constants.js";
32
34
  import { resolveAssetsDir } from "./utils/asset-prefix.js";
33
35
  import { RESOLVED_VIRTUAL_GOOGLE_FONTS, VIRTUAL_GOOGLE_FONTS, createGoogleFontsPlugin, createLocalFontsPlugin, generateGoogleFontsVirtualModule, parseStaticObjectLiteral } from "./plugins/fonts.js";
34
- import { getViteMajorVersion } from "./utils/vite-version.js";
36
+ import { getDepOptimizeNodeEnvOptions, getViteMajorVersion, serializeViteDefine } from "./utils/vite-version.js";
35
37
  import { createRscCompatibilityId, findNextConfigPath, loadNextConfig, resolveNextConfig, resolveNextConfigInput } from "./config/next-config.js";
36
38
  import { isNextDataPathname, parseNextDataPathname } from "./server/pages-data-route.js";
37
39
  import { precompressAssets } from "./build/precompress.js";
@@ -41,6 +43,7 @@ import { collectInlineCssManifest, injectInlineCssManifestGlobal } from "./build
41
43
  import { installDevStackSourcemapMiddleware } from "./server/dev-stack-sourcemap.js";
42
44
  import { runPagesRequest } from "./server/pages-request-pipeline.js";
43
45
  import { pagesRouteHasPriorityOverAppRoute, validateHybridRouteConflicts } from "./server/hybrid-route-priority.js";
46
+ import { VIRTUAL_MODULE_ID_RE } from "./utils/virtual-module.js";
44
47
  import { renderVinextBuiltUrl } from "./utils/built-asset-url.js";
45
48
  import { asyncHooksStubPlugin } from "./plugins/async-hooks-stub.js";
46
49
  import { clientReferenceDedupPlugin } from "./plugins/client-reference-dedup.js";
@@ -63,7 +66,7 @@ import { resolvePostcssStringPlugins } from "./plugins/postcss.js";
63
66
  import { buildSassPreprocessorOptions, createSassAwareFileSystemLoader, createSassTildeImporter } from "./plugins/sass.js";
64
67
  import { createClientAssetFileNames, createClientCodeSplittingConfig, createClientFileNameConfig, createClientManualChunks, createClientOutputConfig, createRscFrameworkChunkOutputConfig, getBuildBundlerOptions, getClientTreeshakeConfigForVite, withBuildBundlerOptions } from "./build/client-build-config.js";
65
68
  import { markCssUrlAssetReferences, restoreDedupedCssAssetReferences } from "./build/css-url-assets.js";
66
- import { stripServerExports } from "./plugins/strip-server-exports.js";
69
+ import { hasExportAllCandidate, stripServerExports, validatePageExports } from "./plugins/strip-server-exports.js";
67
70
  import { removeConsoleCalls } from "./plugins/remove-console.js";
68
71
  import { createImportMetaUrlPlugin } from "./plugins/import-meta-url.js";
69
72
  import { createRequireContextPlugin } from "./plugins/require-context.js";
@@ -78,12 +81,16 @@ import fs from "node:fs";
78
81
  import path from "node:path";
79
82
  import { loadEnv, parseAst, transformWithOxc } from "vite";
80
83
  import { pathToFileURL } from "node:url";
81
- import { randomBytes, randomUUID } from "node:crypto";
84
+ import { createHash, randomBytes, randomUUID } from "node:crypto";
82
85
  import commonjs from "vite-plugin-commonjs";
83
86
  import MagicString from "magic-string";
84
87
  import tsconfigPaths from "vite-tsconfig-paths";
85
88
  //#region src/index.ts
86
89
  installSocketErrorBackstop();
90
+ function getCacheDirPrefix(cacheDir) {
91
+ const normalizedCacheDir = normalizePathSeparators(cacheDir);
92
+ return normalizedCacheDir.endsWith("/") ? normalizedCacheDir : `${normalizedCacheDir}/`;
93
+ }
87
94
  function isInsideDirectory(dir, filePath) {
88
95
  const relativePath = path.relative(dir, filePath);
89
96
  return relativePath !== "" && !relativePath.startsWith("..") && !path.isAbsolute(relativePath);
@@ -162,6 +169,19 @@ function resolveTsconfigPathCandidate(candidate) {
162
169
  for (const item of candidates) if (fs.existsSync(item) && fs.statSync(item).isFile()) return item;
163
170
  return null;
164
171
  }
172
+ /**
173
+ * Normalize a tsconfig `extends` field into a list of specifier strings.
174
+ *
175
+ * TypeScript 5.0+ allows `extends` to be either a string or an array of
176
+ * strings. Matches Next.js's handling in
177
+ * packages/next/src/build/next-config-ts/transpile-config.ts, where parents
178
+ * are iterated in order and later entries override earlier ones.
179
+ */
180
+ function normalizeTsconfigExtends(extendsField) {
181
+ if (typeof extendsField === "string") return [extendsField];
182
+ if (Array.isArray(extendsField)) return extendsField.filter((value) => typeof value === "string");
183
+ return [];
184
+ }
165
185
  function resolveTsconfigExtends(configPath, specifier) {
166
186
  const fromDir = path.dirname(configPath);
167
187
  if (specifier.startsWith(".") || specifier.startsWith("/") || specifier.startsWith("\\")) return resolveTsconfigPathCandidate(path.resolve(fromDir, specifier));
@@ -221,9 +241,12 @@ function loadTsconfigPathAliases(configPath, projectRoot, seen = /* @__PURE__ */
221
241
  }
222
242
  if (!parsed) return {};
223
243
  let aliases = {};
224
- if (typeof parsed.extends === "string") {
225
- const extendedPath = resolveTsconfigExtends(normalizedPath, parsed.extends);
226
- if (extendedPath) aliases = loadTsconfigPathAliases(extendedPath, projectRoot, seen);
244
+ for (const extendsSpecifier of normalizeTsconfigExtends(parsed.extends)) {
245
+ const extendedPath = resolveTsconfigExtends(normalizedPath, extendsSpecifier);
246
+ if (extendedPath) aliases = {
247
+ ...aliases,
248
+ ...loadTsconfigPathAliases(extendedPath, projectRoot, seen)
249
+ };
227
250
  }
228
251
  const compilerOptions = isUnknownRecord(parsed.compilerOptions) ? parsed.compilerOptions : null;
229
252
  const pathsConfig = compilerOptions && isUnknownRecord(compilerOptions.paths) ? compilerOptions.paths : null;
@@ -284,10 +307,21 @@ const RESOLVED_ROOT_PARAMS = "\0virtual:vinext-root-params";
284
307
  const RESOLVED_CACHE_ADAPTERS = "\0" + VIRTUAL_CACHE_ADAPTERS;
285
308
  /** Virtual module for composed instrumentation-client bootstrap. */
286
309
  const VIRTUAL_INSTRUMENTATION_CLIENT = "private-next-instrumentation-client";
287
- const RESOLVED_INSTRUMENTATION_CLIENT = `\0${VIRTUAL_INSTRUMENTATION_CLIENT}.mjs`;
310
+ const RESOLVED_INSTRUMENTATION_CLIENT = `${VIRTUAL_INSTRUMENTATION_CLIENT}.mjs`;
288
311
  /** Image file extensions handled by the vinext:image-imports plugin.
289
312
  * Shared between the Rolldown hook filter and the transform handler regex. */
290
313
  const IMAGE_EXTS = "png|jpe?g|gif|webp|avif|svg|ico|bmp|tiff?";
314
+ /** Matches a trailing image extension on an import path. Built once: `IMAGE_EXTS`
315
+ * is constant, so there is no need to recompile this per transform invocation. */
316
+ const IMAGE_EXT_RE = new RegExp(`\\.(${IMAGE_EXTS})$`);
317
+ function createStaticImageAsset(imagePath) {
318
+ const source = fs.readFileSync(imagePath);
319
+ const extension = path.extname(imagePath);
320
+ return {
321
+ fileName: `media/${path.basename(imagePath, extension)}.${createHash("sha256").update(source).digest("hex").slice(0, 8)}${extension}`,
322
+ source
323
+ };
324
+ }
291
325
  /**
292
326
  * Absolute path to vinext's shims directory, with a trailing slash. Normalized
293
327
  * to forward slashes because it is prefix-matched against Vite module ids (which
@@ -297,6 +331,8 @@ const IMAGE_EXTS = "png|jpe?g|gif|webp|avif|svg|ico|bmp|tiff?";
297
331
  */
298
332
  const _shimsDir = normalizePathSeparators(path.resolve(__dirname, "shims")) + "/";
299
333
  const _fontGoogleShimPath = resolveShimModulePath(_shimsDir, "font-google");
334
+ const _appRscHandlerPath = resolveShimModulePath(path.resolve(__dirname, "server"), "app-rsc-handler");
335
+ const _canExternalizeAppRscHandler = _appRscHandlerPath.endsWith(".js");
300
336
  function isValidExportIdentifier(name) {
301
337
  return /^[$A-Z_a-z][$\w]*$/.test(name);
302
338
  }
@@ -394,6 +430,7 @@ function vinext(options = {}) {
394
430
  const viteMajorVersion = getViteMajorVersion();
395
431
  let root;
396
432
  let pagesDir;
433
+ let canonicalPagesDir;
397
434
  let appDir;
398
435
  let hasAppDir = false;
399
436
  let hasPagesDir = false;
@@ -410,9 +447,23 @@ function vinext(options = {}) {
410
447
  let rscCompatibilityId;
411
448
  const draftModeSecret = randomUUID();
412
449
  const sassComposesLoader = createSassAwareFileSystemLoader();
450
+ const typeofWindowIdFilter = { exclude: /(?!)/ };
413
451
  let rscClassificationManifest = null;
414
452
  const shimsDir = path.resolve(__dirname, "shims");
415
- const canonicalize = (p) => tryRealpathSync(p) ?? p;
453
+ const canonicalize = (p) => normalizePathSeparators(tryRealpathSync(p) ?? p);
454
+ const pageTransformCanonicalPaths = /* @__PURE__ */ new Map();
455
+ const canonicalizePageTransformPath = (modulePath) => {
456
+ const cached = pageTransformCanonicalPaths.get(modulePath);
457
+ if (cached) return cached;
458
+ const canonicalPath = canonicalize(modulePath);
459
+ pageTransformCanonicalPaths.set(modulePath, canonicalPath);
460
+ return canonicalPath;
461
+ };
462
+ const isWithinPagesDirectory = (modulePath) => modulePath === pagesDir || modulePath.startsWith(`${pagesDir}/`) || modulePath === canonicalPagesDir || modulePath.startsWith(`${canonicalPagesDir}/`);
463
+ const isApiPage = (canonicalId) => {
464
+ const relativePath = fileMatcher.stripExtension(canonicalId.slice(canonicalPagesDir.length));
465
+ return relativePath === "/api" || relativePath.startsWith("/api/");
466
+ };
416
467
  const dynamicShimPaths = new Set([
417
468
  resolveShimModulePath(shimsDir, "headers"),
418
469
  resolveShimModulePath(shimsDir, "server"),
@@ -481,6 +532,9 @@ function vinext(options = {}) {
481
532
  });
482
533
  }
483
534
  const imageImportDimCache = /* @__PURE__ */ new Map();
535
+ const staticImageAssets = /* @__PURE__ */ new Map();
536
+ const staticImageImportsByModule = /* @__PURE__ */ new Map();
537
+ const writtenStaticImageFiles = /* @__PURE__ */ new Set();
484
538
  let mdxDelegate = null;
485
539
  let mdxDelegatePromise = null;
486
540
  let hasUserMdxPlugin = false;
@@ -520,22 +574,25 @@ function vinext(options = {}) {
520
574
  ...viteMajorVersion >= 8 ? [{
521
575
  name: "vinext:jsx-in-js",
522
576
  enforce: "pre",
523
- async transform(code, id) {
524
- const cleanId = id.split("?")[0];
525
- if (!/\.(m?js)$/.test(cleanId)) return;
526
- if (cleanId.includes("/node_modules/")) {
527
- if (!code.includes("use client") && !code.includes("use server")) return;
528
- if (!hasReactDirective(code)) return;
577
+ transform: {
578
+ filter: { id: /\.m?js(?:\?.*)?$/ },
579
+ async handler(code, id) {
580
+ const cleanId = id.split("?")[0];
581
+ if (isInsideDirectory(__dirname, cleanId)) return;
582
+ if (cleanId.includes("/node_modules/")) {
583
+ if (!code.includes("use client") && !code.includes("use server")) return;
584
+ if (!hasReactDirective(code)) return;
585
+ }
586
+ const result = await transformWithOxc(code, id, {
587
+ lang: "jsx",
588
+ jsx: { runtime: "automatic" },
589
+ sourcemap: true
590
+ });
591
+ return {
592
+ code: result.code,
593
+ map: result.map
594
+ };
529
595
  }
530
- const result = await transformWithOxc(code, id, {
531
- lang: "jsx",
532
- jsx: { runtime: "automatic" },
533
- sourcemap: true
534
- });
535
- return {
536
- code: result.code,
537
- map: result.map
538
- };
539
596
  }
540
597
  }] : [],
541
598
  createMiddlewareServerOnlyPlugin({
@@ -572,6 +629,7 @@ function vinext(options = {}) {
572
629
  else baseDir = root;
573
630
  }
574
631
  pagesDir = path.posix.join(baseDir, "pages");
632
+ canonicalPagesDir = canonicalize(pagesDir);
575
633
  appDir = path.posix.join(baseDir, "app");
576
634
  hasPagesDir = fs.existsSync(pagesDir);
577
635
  hasAppDir = !options.disableAppRouter && fs.existsSync(appDir);
@@ -605,7 +663,10 @@ function vinext(options = {}) {
605
663
  clientInjectModule = instrumentationClientInjects.length ? generateInstrumentationClientInjectModule(instrumentationClientInjects, instrumentationClientPath, INSTRUMENTATION_CLIENT_EMPTY_MODULE) : null;
606
664
  if (env?.command === "build") await writeRouteTypes();
607
665
  const defines = getNextPublicEnvDefines();
608
- if (!config.define || typeof config.define !== "object" || !("process.env.NODE_ENV" in config.define)) defines["process.env.NODE_ENV"] = JSON.stringify(resolvedNodeEnv);
666
+ const userNodeEnvDefine = config.define?.["process.env.NODE_ENV"];
667
+ const hasUserNodeEnvDefine = Object.hasOwn(config.define ?? {}, "process.env.NODE_ENV");
668
+ const nodeEnvDefine = hasUserNodeEnvDefine ? serializeViteDefine(userNodeEnvDefine) : JSON.stringify(resolvedNodeEnv);
669
+ if (!hasUserNodeEnvDefine) defines["process.env.NODE_ENV"] = nodeEnvDefine;
609
670
  for (const [key, value] of Object.entries(nextConfig.env)) {
610
671
  if (key === "NODE_ENV") continue;
611
672
  defines[`process.env.${key}`] = JSON.stringify(value);
@@ -836,10 +897,15 @@ function vinext(options = {}) {
836
897
  if (shimBase !== void 0) return resolveShimModulePath(shimsDir, shimBase);
837
898
  }
838
899
  };
900
+ const depOptimizeNodeEnvOptions = getDepOptimizeNodeEnvOptions(viteMajorVersion, nodeEnvDefine);
839
901
  viteConfig.optimizeDeps = {
840
902
  exclude: mergeOptimizeDepsExclude(incomingExclude, VINEXT_OPTIMIZE_DEPS_EXCLUDE, ["@tailwindcss/oxide"]),
841
903
  ...incomingInclude.length > 0 ? { include: incomingInclude } : {},
842
- rolldownOptions: { plugins: [depOptimizeAliasPlugin] }
904
+ ...depOptimizeNodeEnvOptions,
905
+ rolldownOptions: {
906
+ ...depOptimizeNodeEnvOptions.rolldownOptions,
907
+ plugins: [depOptimizeAliasPlugin]
908
+ }
843
909
  };
844
910
  const pagesOptimizeEntries = !hasAppDir ? [...hasPagesDir ? [toRelativeFileEntry(root, pagesDir) + "/**/*.{tsx,ts,jsx,js}"] : [], ...[instrumentationPath, instrumentationClientPath].flatMap((entry) => entry ? [toRelativeFileEntry(root, entry)] : [])] : [];
845
911
  if (hasAppDir) {
@@ -855,13 +921,16 @@ function vinext(options = {}) {
855
921
  "satori",
856
922
  "@resvg/resvg-js",
857
923
  "yoga-wasm-web",
924
+ ...env?.command === "serve" && _canExternalizeAppRscHandler ? ["vinext/server/app-rsc-handler"] : [],
858
925
  ...userSsrExternal
859
926
  ],
860
927
  ...userSsrExternal === true ? {} : { noExternal: true }
861
928
  } },
862
929
  optimizeDeps: {
863
930
  exclude: mergeOptimizeDepsExclude(incomingExclude, VINEXT_OPTIMIZE_DEPS_EXCLUDE),
864
- entries: optimizeEntries
931
+ entries: optimizeEntries,
932
+ include: [...new Set([...incomingInclude, "react-server-dom-webpack/static.edge"])],
933
+ ...depOptimizeNodeEnvOptions
865
934
  },
866
935
  build: {
867
936
  outDir: options.rscOutDir ?? "dist/server",
@@ -878,7 +947,8 @@ function vinext(options = {}) {
878
947
  } },
879
948
  optimizeDeps: {
880
949
  exclude: mergeOptimizeDepsExclude(incomingExclude, VINEXT_OPTIMIZE_DEPS_EXCLUDE, ["ipaddr.js"], userSsrExternal === true ? SSR_EXTERNAL_REACT_ENTRIES : []),
881
- entries: optimizeEntries
950
+ entries: optimizeEntries,
951
+ ...depOptimizeNodeEnvOptions
882
952
  },
883
953
  build: {
884
954
  outDir: options.ssrOutDir ?? "dist/server/ssr",
@@ -951,7 +1021,10 @@ function vinext(options = {}) {
951
1021
  ],
952
1022
  noExternal: true
953
1023
  },
954
- optimizeDeps: { exclude: ["ipaddr.js"] },
1024
+ optimizeDeps: {
1025
+ exclude: ["ipaddr.js"],
1026
+ ...depOptimizeNodeEnvOptions
1027
+ },
955
1028
  build: {
956
1029
  outDir: "dist/server",
957
1030
  ...withBuildBundlerOptions(viteMajorVersion, {
@@ -975,6 +1048,8 @@ function vinext(options = {}) {
975
1048
  return null;
976
1049
  },
977
1050
  async configResolved(config) {
1051
+ const cacheDirPrefix = getCacheDirPrefix(config.cacheDir);
1052
+ typeofWindowIdFilter.exclude = new RegExp(`^${escapeRegExp(cacheDirPrefix)}`);
978
1053
  sassComposesLoader.setResolvedConfig(config);
979
1054
  if (config.command === "build" && hasAppDir && hasPagesDir) {
980
1055
  const [appRoutes, pageRoutes, apiRoutes] = await Promise.all([
@@ -1011,9 +1086,16 @@ function vinext(options = {}) {
1011
1086
  }));
1012
1087
  },
1013
1088
  resolveId: {
1014
- filter: { id: /(?:next\/|vinext\/shims\/|virtual:vinext-|@vercel\/og(?:\.js)?$)/ },
1089
+ filter: { id: /(?:next\/|vinext\/(?:shims\/|server\/app-rsc-handler)|virtual:vinext-|@vercel\/og(?:\.js)?$)/ },
1015
1090
  handler(id, importer) {
1016
1091
  const cleanId = id.startsWith("\0") ? id.slice(1) : id;
1092
+ if (cleanId === "vinext/server/app-rsc-handler") {
1093
+ if (_canExternalizeAppRscHandler && this.environment?.name === "rsc" && this.environment.config?.command === "serve") return {
1094
+ id: _appRscHandlerPath,
1095
+ external: true
1096
+ };
1097
+ return _appRscHandlerPath;
1098
+ }
1017
1099
  if (isVercelOgImport(cleanId) && !isVinextOgShimImporter(importer)) return resolveShimModulePath(_shimsDir, "og");
1018
1100
  if (cleanId.startsWith("vinext/shims/")) return resolveShimModulePath(_shimsDir, stripJsExtension(stripViteModuleQuery(cleanId.slice(13))));
1019
1101
  if (cleanId === VIRTUAL_SERVER_ENTRY) return RESOLVED_SERVER_ENTRY;
@@ -1132,14 +1214,19 @@ function vinext(options = {}) {
1132
1214
  name: "vinext:css-url-assets-mark",
1133
1215
  enforce: "pre",
1134
1216
  apply: "build",
1135
- transform(code, id) {
1136
- if (this.environment?.name !== "client") return null;
1137
- const marked = markCssUrlAssetReferences(code, id);
1138
- if (marked === null) return null;
1139
- return {
1140
- code: marked,
1141
- map: null
1142
- };
1217
+ transform: {
1218
+ filter: {
1219
+ id: /\.(?:css|scss|sass|less|styl|stylus)(?:\?|$)/i,
1220
+ code: "url("
1221
+ },
1222
+ handler(code, id) {
1223
+ const marked = markCssUrlAssetReferences(code, id);
1224
+ if (marked === null) return null;
1225
+ return {
1226
+ code: marked,
1227
+ map: null
1228
+ };
1229
+ }
1143
1230
  }
1144
1231
  },
1145
1232
  {
@@ -1164,7 +1251,6 @@ function vinext(options = {}) {
1164
1251
  enforce: "post",
1165
1252
  apply: "build",
1166
1253
  generateBundle(_options, bundle) {
1167
- if (this.environment?.name !== "client") return;
1168
1254
  restoreDedupedCssAssetReferences(bundle, (asset) => {
1169
1255
  this.emitFile({
1170
1256
  type: "asset",
@@ -1242,18 +1328,20 @@ function vinext(options = {}) {
1242
1328
  `export const addTransitionType = _React.addTransitionType || function addTransitionType() {};`
1243
1329
  ].join("\n");
1244
1330
  },
1245
- transform(code, id) {
1246
- if (id.includes("node_modules")) return null;
1247
- if (id.startsWith("\0")) return null;
1248
- if (!/\.(tsx?|jsx?|mjs)$/.test(id)) return null;
1249
- if (!(code.includes("ViewTransition") || code.includes("addTransitionType")) || !/from\s+['"]react['"]/.test(code)) return null;
1250
- if (!/import\s*\{[^}]*(ViewTransition|addTransitionType)[^}]*\}\s*from\s*['"]react['"]/.test(code)) return null;
1251
- const result = code.replace(/from\s*['"]react['"]/g, "from \"virtual:vinext-react-canary\"");
1252
- if (result !== code) return {
1253
- code: result,
1254
- map: null
1255
- };
1256
- return null;
1331
+ transform: {
1332
+ filter: {
1333
+ id: {
1334
+ include: /\.(tsx?|jsx?|mjs)$/,
1335
+ exclude: [/node_modules/, VIRTUAL_MODULE_ID_RE]
1336
+ },
1337
+ code: /import\s*\{[^}]*(ViewTransition|addTransitionType)[^}]*\}\s*from\s*['"]react['"]/
1338
+ },
1339
+ handler(code) {
1340
+ return {
1341
+ code: code.replace(/from\s*['"]react['"]/g, "from \"virtual:vinext-react-canary\""),
1342
+ map: null
1343
+ };
1344
+ }
1257
1345
  }
1258
1346
  },
1259
1347
  {
@@ -1435,6 +1523,51 @@ function vinext(options = {}) {
1435
1523
  });
1436
1524
  installDevStackSourcemapMiddleware(server);
1437
1525
  return () => {
1526
+ const viteFilesystemMiddlewares = server.middlewares.stack.filter(({ handle }) => {
1527
+ const name = typeof handle === "function" ? handle.name : "";
1528
+ return name === "viteServePublicMiddleware" || name === "viteServeStaticMiddleware";
1529
+ }).map(({ handle }) => handle).filter((handle) => typeof handle === "function");
1530
+ const serveRewrittenViteFilesystemRoute = async (req, res, requestPathname, stagedHeaders) => {
1531
+ const originalUrl = req.url;
1532
+ const originalStatusCode = res.statusCode;
1533
+ const originalStatusMessage = res.statusMessage;
1534
+ const originalHeaders = res.getHeaders();
1535
+ req.url = requestPathname;
1536
+ for (const [key, value] of Object.entries(stagedHeaders)) res.setHeader(key, value);
1537
+ const restore = () => {
1538
+ req.url = originalUrl;
1539
+ res.statusCode = originalStatusCode;
1540
+ res.statusMessage = originalStatusMessage;
1541
+ for (const key of Object.keys(res.getHeaders())) res.removeHeader(key);
1542
+ for (const [key, value] of Object.entries(originalHeaders)) if (value !== void 0) res.setHeader(key, value);
1543
+ };
1544
+ try {
1545
+ for (const middleware of viteFilesystemMiddlewares) if (await new Promise((resolve, reject) => {
1546
+ let settled = false;
1547
+ const settle = (value, error) => {
1548
+ if (settled) return;
1549
+ settled = true;
1550
+ res.off("finish", onServed);
1551
+ res.off("close", onServed);
1552
+ if (error) reject(error);
1553
+ else resolve(value);
1554
+ };
1555
+ const onServed = () => settle("served");
1556
+ res.once("finish", onServed);
1557
+ res.once("close", onServed);
1558
+ middleware(req, res, (error) => settle("next", error));
1559
+ if (res.writableEnded) settle("served");
1560
+ }) === "served") {
1561
+ req.url = originalUrl;
1562
+ return true;
1563
+ }
1564
+ } catch (error) {
1565
+ restore();
1566
+ throw error;
1567
+ }
1568
+ restore();
1569
+ return false;
1570
+ };
1438
1571
  if (instrumentationPath && !hasAppDir) runInstrumentation(getPagesRunner(), instrumentationPath).catch((err) => {
1439
1572
  console.error("[vinext] Instrumentation error:", err);
1440
1573
  });
@@ -1556,6 +1689,7 @@ function vinext(options = {}) {
1556
1689
  return;
1557
1690
  }
1558
1691
  }
1692
+ if (hasCloudflarePlugin) return next();
1559
1693
  let isDataReq = false;
1560
1694
  if (isNextDataPathname(pathname)) {
1561
1695
  const devBuildId = nextConfig?.buildId ?? process.env.__VINEXT_BUILD_ID ?? "development";
@@ -1575,10 +1709,17 @@ function vinext(options = {}) {
1575
1709
  return;
1576
1710
  }
1577
1711
  }
1578
- if (pathname.includes(".") && !pathname.endsWith(".html")) return next();
1579
- if (hasCloudflarePlugin) return next();
1712
+ const filePathMatchesRewrite = [
1713
+ ...nextConfig?.rewrites.beforeFiles ?? [],
1714
+ ...nextConfig?.rewrites.afterFiles ?? [],
1715
+ ...nextConfig?.rewrites.fallback ?? []
1716
+ ].some((rewrite) => matchesRewriteSource(pathname, rewrite, {
1717
+ basePath: bp,
1718
+ hadBasePath: true
1719
+ }));
1720
+ if (pathname.includes(".") && !pathname.endsWith(".html") && !filePathMatchesRewrite) return next();
1580
1721
  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)])));
1581
- const isDataRequest = rawHeaders.get("x-nextjs-data") === "1";
1722
+ const isDataRequest = isDataReq;
1582
1723
  const nodeRequestHeaders = filterInternalHeaders(rawHeaders);
1583
1724
  for (const header of INTERNAL_HEADERS) delete req.headers[header];
1584
1725
  for (const header of VINEXT_INTERNAL_HEADERS) delete req.headers[header];
@@ -1649,6 +1790,10 @@ function vinext(options = {}) {
1649
1790
  externalInit.duplex = "half";
1650
1791
  }
1651
1792
  return proxyExternalRequest(new Request(new URL(url, requestOrigin), externalInit), externalUrl);
1793
+ },
1794
+ serveFilesystemRoute: async (requestPathname, stagedHeaders, phase) => {
1795
+ if (phase === "direct" || req.method !== "GET" && req.method !== "HEAD" || requestPathname === "/" || requestPathname === "/api" || requestPathname.startsWith("/api/")) return false;
1796
+ return serveRewrittenViteFilesystemRoute(req, res, requestPathname, stagedHeaders);
1652
1797
  }
1653
1798
  });
1654
1799
  if (pipelineResult.type === "response") {
@@ -1714,38 +1859,63 @@ function vinext(options = {}) {
1714
1859
  }
1715
1860
  },
1716
1861
  {
1717
- name: "vinext:validate-server-only-client-imports",
1862
+ name: "vinext:validate-page-exports",
1718
1863
  transform: {
1719
1864
  filter: {
1720
- id: /\.(tsx?|jsx?|mjs)$/,
1721
- code: "server-only"
1865
+ id: { exclude: VIRTUAL_MODULE_ID_RE },
1866
+ code: /\bexport\b[\s\S]*\*/
1722
1867
  },
1723
1868
  handler(code, id) {
1724
1869
  if (this.environment?.name !== "client") return null;
1725
- if (id.startsWith("\0")) return null;
1726
- if (getLeadingReactDirective(code) === "use server") return null;
1727
- if (!hasServerOnlyMarkerImport(code)) return null;
1728
- throw new Error(`You're importing a module that depends on "server-only". This API is only available in Server Components in the App Router, but this module is reachable from a client bundle.`);
1870
+ if (!hasPagesDir || !hasExportAllCandidate(code)) return null;
1871
+ const modulePath = stripViteModuleQuery(id);
1872
+ if (!isWithinPagesDirectory(modulePath)) return null;
1873
+ const canonicalId = canonicalizePageTransformPath(modulePath);
1874
+ if (!isWithinPagesDirectory(canonicalId)) return null;
1875
+ if (!fileMatcher.isPageFile(canonicalId)) return null;
1876
+ if (isApiPage(canonicalId)) return null;
1877
+ validatePageExports(code);
1878
+ return null;
1729
1879
  }
1730
1880
  }
1731
1881
  },
1732
1882
  {
1733
1883
  name: "vinext:strip-server-exports",
1734
1884
  transform: {
1735
- filter: { id: /\.(tsx?|jsx?|mjs)$/ },
1885
+ filter: {
1886
+ id: { exclude: VIRTUAL_MODULE_ID_RE },
1887
+ code: /getServerSideProps|getStaticProps|getStaticPaths|unstable_getServerProps|unstable_getServerSideProps|unstable_getStaticProps|unstable_getStaticPaths/
1888
+ },
1736
1889
  handler(code, id) {
1737
1890
  if (this.environment?.name !== "client") return null;
1738
1891
  if (!hasPagesDir) return null;
1739
- if (!id.startsWith(pagesDir)) return null;
1740
- const relativePath = id.slice(pagesDir.length);
1741
- if (relativePath.startsWith("/api/") || relativePath === "/api") return null;
1742
- if (/\/_(?:app|document|error)\b/.test(relativePath)) return null;
1743
- const result = stripServerExports(code);
1744
- if (!result) return null;
1745
- return {
1746
- code: result,
1747
- map: null
1748
- };
1892
+ const modulePath = stripViteModuleQuery(id);
1893
+ if (!isWithinPagesDirectory(modulePath)) return null;
1894
+ const canonicalId = canonicalizePageTransformPath(modulePath);
1895
+ if (!isWithinPagesDirectory(canonicalId)) return null;
1896
+ if (!fileMatcher.isPageFile(canonicalId)) return null;
1897
+ const relativePath = canonicalId.slice(canonicalPagesDir.length);
1898
+ if (isApiPage(canonicalId)) return null;
1899
+ if (/^\/(?:_app|_document|_error)(?:\.[^/]*)?$/.test(relativePath)) return null;
1900
+ return stripServerExports(code);
1901
+ }
1902
+ }
1903
+ },
1904
+ {
1905
+ name: "vinext:validate-server-only-client-imports",
1906
+ transform: {
1907
+ filter: {
1908
+ id: {
1909
+ include: /\.(tsx?|jsx?|mjs)$/,
1910
+ exclude: VIRTUAL_MODULE_ID_RE
1911
+ },
1912
+ code: "server-only"
1913
+ },
1914
+ handler(code) {
1915
+ if (this.environment?.name !== "client") return null;
1916
+ if (getLeadingReactDirective(code) === "use server") return null;
1917
+ if (!hasServerOnlyMarkerImport(code)) return null;
1918
+ throw new Error(`You're importing a module that depends on "server-only". This API is only available in Server Components in the App Router, but this module is reachable from a client bundle.`);
1749
1919
  }
1750
1920
  }
1751
1921
  },
@@ -1753,17 +1923,17 @@ function vinext(options = {}) {
1753
1923
  name: "vinext:remove-console",
1754
1924
  apply: "build",
1755
1925
  transform: {
1756
- filter: { id: /\.(tsx?|jsx?|mjs)$/ },
1757
- handler(code, id) {
1926
+ filter: {
1927
+ id: {
1928
+ include: /\.(tsx?|jsx?|mjs)$/,
1929
+ exclude: /\/node_modules\//
1930
+ },
1931
+ code: /\bconsole\b/
1932
+ },
1933
+ handler(code) {
1758
1934
  if (this.environment?.name !== "client") return null;
1759
1935
  if (!nextConfig.removeConsole) return null;
1760
- if (id.includes("/node_modules/")) return null;
1761
- const result = removeConsoleCalls(code, nextConfig.removeConsole);
1762
- if (!result) return null;
1763
- return {
1764
- code: result,
1765
- map: null
1766
- };
1936
+ return removeConsoleCalls(code, nextConfig.removeConsole);
1767
1937
  }
1768
1938
  }
1769
1939
  },
@@ -1771,8 +1941,13 @@ function vinext(options = {}) {
1771
1941
  name: "vinext:typeof-window",
1772
1942
  enforce: "post",
1773
1943
  transform: {
1774
- filter: { code: /typeof\s+window/ },
1775
- handler(code) {
1944
+ filter: {
1945
+ id: typeofWindowIdFilter,
1946
+ code: /typeof\s+window/
1947
+ },
1948
+ handler(code, id) {
1949
+ const cacheDirPrefix = getCacheDirPrefix(this.environment.config.cacheDir);
1950
+ if (normalizePathSeparators(id).startsWith(cacheDirPrefix)) return null;
1776
1951
  return replaceTypeofWindow(code, getTypeofWindowReplacement(this.environment));
1777
1952
  }
1778
1953
  }
@@ -1792,16 +1967,37 @@ function vinext(options = {}) {
1792
1967
  name: "vinext:image-imports",
1793
1968
  enforce: "pre",
1794
1969
  _dimCache: imageImportDimCache,
1970
+ buildStart() {
1971
+ imageImportDimCache.clear();
1972
+ staticImageAssets.clear();
1973
+ },
1974
+ watchChange(id) {
1975
+ const key = normalizePathSeparators(id);
1976
+ imageImportDimCache.delete(key);
1977
+ staticImageAssets.delete(key);
1978
+ staticImageImportsByModule.delete(key);
1979
+ },
1795
1980
  resolveId: {
1796
- filter: { id: /\?vinext-meta$/ },
1981
+ filter: { id: /\?vinext-(?:image-url|meta)$/ },
1797
1982
  handler(source, _importer) {
1798
- if (!source.endsWith("?vinext-meta")) return null;
1799
- return `\0vinext-image-meta:${source.replace("?vinext-meta", "")}`;
1983
+ if (source.endsWith("?vinext-image-url")) return `\0vinext-image-url:${source.slice(0, -17)}`;
1984
+ if (source.endsWith("?vinext-meta")) return `\0vinext-image-meta:${source.slice(0, -12)}`;
1985
+ return null;
1800
1986
  }
1801
1987
  },
1802
1988
  async load(id) {
1989
+ if (id.startsWith("\0vinext-image-url:")) {
1990
+ const imagePath = id.replace("\0vinext-image-url:", "");
1991
+ this.addWatchFile(imagePath);
1992
+ if (this.environment.config.command === "serve") return `import url from ${JSON.stringify(imagePath + "?url")}; export default url;`;
1993
+ const asset = createStaticImageAsset(imagePath);
1994
+ staticImageAssets.set(imagePath, asset);
1995
+ const builtFileName = `${resolveAssetsDir(nextConfig.assetPrefix)}/${asset.fileName}`;
1996
+ return `export default ${JSON.stringify(renderVinextBuiltUrl(builtFileName, nextConfig.assetPrefix, nextConfig.deploymentId))};`;
1997
+ }
1803
1998
  if (!id.startsWith("\0vinext-image-meta:")) return null;
1804
1999
  const imagePath = id.replace("\0vinext-image-meta:", "");
2000
+ this.addWatchFile(imagePath);
1805
2001
  const cache = imageImportDimCache;
1806
2002
  let dims = cache.get(imagePath);
1807
2003
  if (!dims) try {
@@ -1824,15 +2020,11 @@ function vinext(options = {}) {
1824
2020
  filter: {
1825
2021
  id: {
1826
2022
  include: /\.(tsx?|jsx?|mjs)$/,
1827
- exclude: /node_modules/
2023
+ exclude: [/node_modules/, VIRTUAL_MODULE_ID_RE]
1828
2024
  },
1829
2025
  code: new RegExp(`import\\s+\\w+\\s+from\\s+['"][^'"]+\\.(${IMAGE_EXTS})['"]`)
1830
2026
  },
1831
2027
  async handler(code, id) {
1832
- if (id.includes("node_modules")) return null;
1833
- if (id.startsWith("\0")) return null;
1834
- if (!id.match(/\.(tsx?|jsx?|mjs)$/)) return null;
1835
- const imageExtRe = new RegExp(`\\.(${IMAGE_EXTS})$`);
1836
2028
  const lang = id.endsWith(".ts") ? "ts" : "tsx";
1837
2029
  let ast;
1838
2030
  try {
@@ -1842,12 +2034,13 @@ function vinext(options = {}) {
1842
2034
  }
1843
2035
  const s = new MagicString(code);
1844
2036
  let hasChanges = false;
2037
+ const imageImports = /* @__PURE__ */ new Set();
1845
2038
  for (const node of ast.body) {
1846
2039
  if (node.type !== "ImportDeclaration") continue;
1847
2040
  const importNode = node;
1848
2041
  const importPath = importNode.source?.value;
1849
2042
  if (typeof importPath !== "string") continue;
1850
- if (!imageExtRe.test(importPath)) continue;
2043
+ if (!IMAGE_EXT_RE.test(importPath)) continue;
1851
2044
  const specifiers = importNode.specifiers ?? [];
1852
2045
  if (specifiers.length !== 1) continue;
1853
2046
  const specifier = specifiers[0];
@@ -1857,18 +2050,45 @@ function vinext(options = {}) {
1857
2050
  const dir = path.dirname(id);
1858
2051
  const absImagePath = normalizePathSeparators(path.resolve(dir, importPath));
1859
2052
  if (!fs.existsSync(absImagePath)) continue;
2053
+ imageImports.add(absImagePath);
1860
2054
  const urlVar = `__vinext_img_url_${varName}`;
1861
2055
  const metaVar = `__vinext_img_meta_${varName}`;
1862
- const replacement = `import ${urlVar} from ${JSON.stringify(importPath)};\nimport ${metaVar} from ${JSON.stringify(absImagePath + "?vinext-meta")};\nconst ${varName} = { src: ${urlVar}, width: ${metaVar}.width, height: ${metaVar}.height };`;
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 };`;
1863
2057
  s.overwrite(importNode.start, importNode.end, replacement);
1864
2058
  hasChanges = true;
1865
2059
  }
1866
- if (!hasChanges) return null;
2060
+ if (!hasChanges) {
2061
+ staticImageImportsByModule.delete(id);
2062
+ return null;
2063
+ }
2064
+ staticImageImportsByModule.set(id, imageImports);
1867
2065
  return {
1868
2066
  code: s.toString(),
1869
2067
  map: s.generateMap({ hires: "boundary" })
1870
2068
  };
1871
2069
  }
2070
+ },
2071
+ writeBundle: {
2072
+ sequential: true,
2073
+ order: "post",
2074
+ handler(outputOptions) {
2075
+ if (this.environment?.name !== "client") return;
2076
+ const clientOutDir = outputOptions.dir ? path.resolve(root, outputOptions.dir) : path.resolve(root, options.clientOutDir ?? "dist/client");
2077
+ const assetsDir = resolveAssetsDir(nextConfig.assetPrefix);
2078
+ const activeImagePaths = new Set(Array.from(staticImageImportsByModule.values()).flatMap((imports) => [...imports]));
2079
+ const nextWrittenFiles = /* @__PURE__ */ new Set();
2080
+ for (const imagePath of activeImagePaths) {
2081
+ if (!fs.existsSync(imagePath)) continue;
2082
+ const asset = staticImageAssets.get(imagePath) ?? createStaticImageAsset(imagePath);
2083
+ const outputPath = path.join(clientOutDir, assetsDir, asset.fileName);
2084
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
2085
+ fs.writeFileSync(outputPath, asset.source);
2086
+ nextWrittenFiles.add(outputPath);
2087
+ }
2088
+ for (const outputPath of writtenStaticImageFiles) if (!nextWrittenFiles.has(outputPath)) fs.rmSync(outputPath, { force: true });
2089
+ writtenStaticImageFiles.clear();
2090
+ for (const outputPath of nextWrittenFiles) writtenStaticImageFiles.add(outputPath);
2091
+ }
1872
2092
  }
1873
2093
  },
1874
2094
  createGoogleFontsPlugin(_fontGoogleShimPath, _shimsDir),
@@ -1881,15 +2101,11 @@ function vinext(options = {}) {
1881
2101
  filter: {
1882
2102
  id: {
1883
2103
  include: /\.(tsx?|jsx?|mjs)$/,
1884
- exclude: /node_modules/
2104
+ exclude: [/node_modules/, VIRTUAL_MODULE_ID_RE]
1885
2105
  },
1886
2106
  code: "use cache"
1887
2107
  },
1888
2108
  async handler(code, id) {
1889
- if (id.includes("node_modules")) return null;
1890
- if (id.startsWith("\0")) return null;
1891
- if (!id.match(/\.(tsx?|jsx?|mjs)$/)) return null;
1892
- if (!code.includes("use cache")) return null;
1893
2109
  const ast = parseAst(code);
1894
2110
  const cacheDirective = ast.body.find((node) => node.type === "ExpressionStatement" && node.expression?.type === "Literal" && typeof node.expression.value === "string" && node.expression.value.startsWith("use cache"));
1895
2111
  function nodeHasInlineCacheDirective(node) {
@@ -2286,54 +2502,56 @@ function vinext(options = {}) {
2286
2502
  {
2287
2503
  name: "vinext:og-font-patch",
2288
2504
  enforce: "pre",
2289
- transform(code, id) {
2290
- if (!id.includes("@vercel/og") || !id.includes("index.edge.js")) return null;
2291
- let result = code;
2292
- const yogaMatch = /H = "data:application\/octet-stream;base64,([A-Za-z0-9+/]+=*)";/.exec(result);
2293
- if (yogaMatch) {
2294
- const yogaBase64 = yogaMatch[1];
2295
- const distDir = path.dirname(id);
2296
- const yogaWasmPath = path.join(distDir, "yoga.wasm");
2297
- if (!fs.existsSync(yogaWasmPath)) fs.writeFileSync(yogaWasmPath, Buffer.from(yogaBase64, "base64"));
2298
- result = result.replace(yogaMatch[0], `H = "";`);
2299
- const YOGA_CALL = `yoga_wasm_base64_esm_default()`;
2300
- const YOGA_CALL_PATCHED = [
2301
- `yoga_wasm_base64_esm_default({ instantiateWasm: function(imports, callback) {`,
2302
- ` __vi_yoga_mod.then(function(mod) {`,
2303
- ` if (mod) {`,
2304
- ` WebAssembly.instantiate(mod, imports).then(function(inst) { callback(inst); });`,
2305
- ` } else {`,
2306
- ` Promise.all([import("node:fs"), import("node:url")]).then(function(mods) {`,
2307
- ` var p = mods[1].fileURLToPath(new URL("./yoga.wasm", import.meta.url));`,
2308
- ` return mods[0].promises.readFile(p).then(function(bytes) {`,
2309
- ` return WebAssembly.instantiate(bytes, imports).then(function(r) { callback(r.instance); });`,
2310
- ` });`,
2311
- ` });`,
2312
- ` }`,
2313
- ` });`,
2314
- ` return {};`,
2315
- `} })`
2316
- ].join("\n");
2317
- result = result.replace(YOGA_CALL, YOGA_CALL_PATCHED);
2318
- result = [`var __vi_yoga_mod = import("./yoga.wasm?module").then(function(m) { return m.default; }).catch(function() { return null; });`].join("\n") + "\n" + result;
2319
- }
2320
- const resvgMatch = /import\s+resvg_wasm\s+from\s+["']\.\/resvg\.wasm\?module["']\s*;?/.exec(result);
2321
- if (resvgMatch) {
2322
- const resvgLoader = [
2323
- `var resvg_wasm = import("./resvg.wasm?module").then(function(m) { return m.default; }).catch(function() {`,
2324
- ` return Promise.all([import("node:fs"), import("node:url")]).then(function(mods) {`,
2325
- ` var p = mods[1].fileURLToPath(new URL("./resvg.wasm", import.meta.url));`,
2326
- ` return mods[0].promises.readFile(p).then(function(buf) { return WebAssembly.compile(buf); });`,
2327
- ` });`,
2328
- `});`
2329
- ].join("\n");
2330
- result = result.replace(resvgMatch[0], resvgLoader);
2505
+ transform: {
2506
+ filter: { id: /@vercel\/og.*index\.edge\.js/ },
2507
+ handler(code, id) {
2508
+ let result = code;
2509
+ const yogaMatch = /H = "data:application\/octet-stream;base64,([A-Za-z0-9+/]+=*)";/.exec(result);
2510
+ if (yogaMatch) {
2511
+ const yogaBase64 = yogaMatch[1];
2512
+ const distDir = path.dirname(id);
2513
+ const yogaWasmPath = path.join(distDir, "yoga.wasm");
2514
+ if (!fs.existsSync(yogaWasmPath)) fs.writeFileSync(yogaWasmPath, Buffer.from(yogaBase64, "base64"));
2515
+ result = result.replace(yogaMatch[0], `H = "";`);
2516
+ const YOGA_CALL = `yoga_wasm_base64_esm_default()`;
2517
+ const YOGA_CALL_PATCHED = [
2518
+ `yoga_wasm_base64_esm_default({ instantiateWasm: function(imports, callback) {`,
2519
+ ` __vi_yoga_mod.then(function(mod) {`,
2520
+ ` if (mod) {`,
2521
+ ` WebAssembly.instantiate(mod, imports).then(function(inst) { callback(inst); });`,
2522
+ ` } else {`,
2523
+ ` Promise.all([import("node:fs"), import("node:url")]).then(function(mods) {`,
2524
+ ` var p = mods[1].fileURLToPath(new URL("./yoga.wasm", import.meta.url));`,
2525
+ ` return mods[0].promises.readFile(p).then(function(bytes) {`,
2526
+ ` return WebAssembly.instantiate(bytes, imports).then(function(r) { callback(r.instance); });`,
2527
+ ` });`,
2528
+ ` });`,
2529
+ ` }`,
2530
+ ` });`,
2531
+ ` return {};`,
2532
+ `} })`
2533
+ ].join("\n");
2534
+ result = result.replace(YOGA_CALL, YOGA_CALL_PATCHED);
2535
+ result = [`var __vi_yoga_mod = import("./yoga.wasm?module").then(function(m) { return m.default; }).catch(function() { return null; });`].join("\n") + "\n" + result;
2536
+ }
2537
+ const resvgMatch = /import\s+resvg_wasm\s+from\s+["']\.\/resvg\.wasm\?module["']\s*;?/.exec(result);
2538
+ if (resvgMatch) {
2539
+ const resvgLoader = [
2540
+ `var resvg_wasm = import("./resvg.wasm?module").then(function(m) { return m.default; }).catch(function() {`,
2541
+ ` return Promise.all([import("node:fs"), import("node:url")]).then(function(mods) {`,
2542
+ ` var p = mods[1].fileURLToPath(new URL("./resvg.wasm", import.meta.url));`,
2543
+ ` return mods[0].promises.readFile(p).then(function(buf) { return WebAssembly.compile(buf); });`,
2544
+ ` });`,
2545
+ `});`
2546
+ ].join("\n");
2547
+ result = result.replace(resvgMatch[0], resvgLoader);
2548
+ }
2549
+ if (result === code) return null;
2550
+ return {
2551
+ code: result,
2552
+ map: null
2553
+ };
2331
2554
  }
2332
- if (result === code) return null;
2333
- return {
2334
- code: result,
2335
- map: null
2336
- };
2337
2555
  }
2338
2556
  }
2339
2557
  ];