vinext 0.0.19 → 0.0.21

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 (71) hide show
  1. package/README.md +1 -1
  2. package/dist/check.d.ts.map +1 -1
  3. package/dist/check.js +2 -1
  4. package/dist/check.js.map +1 -1
  5. package/dist/cli.js +4 -4
  6. package/dist/cli.js.map +1 -1
  7. package/dist/config/next-config.d.ts +6 -0
  8. package/dist/config/next-config.d.ts.map +1 -1
  9. package/dist/config/next-config.js.map +1 -1
  10. package/dist/deploy.d.ts.map +1 -1
  11. package/dist/deploy.js +15 -1
  12. package/dist/deploy.js.map +1 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +142 -18
  15. package/dist/index.js.map +1 -1
  16. package/dist/init.d.ts +9 -1
  17. package/dist/init.d.ts.map +1 -1
  18. package/dist/init.js +31 -2
  19. package/dist/init.js.map +1 -1
  20. package/dist/server/app-dev-server.d.ts.map +1 -1
  21. package/dist/server/app-dev-server.js +104 -13
  22. package/dist/server/app-dev-server.js.map +1 -1
  23. package/dist/server/dev-server.d.ts.map +1 -1
  24. package/dist/server/dev-server.js +31 -5
  25. package/dist/server/dev-server.js.map +1 -1
  26. package/dist/server/image-optimization.d.ts +17 -3
  27. package/dist/server/image-optimization.d.ts.map +1 -1
  28. package/dist/server/image-optimization.js +31 -13
  29. package/dist/server/image-optimization.js.map +1 -1
  30. package/dist/server/middleware.d.ts.map +1 -1
  31. package/dist/server/middleware.js +13 -4
  32. package/dist/server/middleware.js.map +1 -1
  33. package/dist/server/prod-server.d.ts.map +1 -1
  34. package/dist/server/prod-server.js +25 -8
  35. package/dist/server/prod-server.js.map +1 -1
  36. package/dist/shims/compat-router.d.ts +12 -0
  37. package/dist/shims/compat-router.d.ts.map +1 -0
  38. package/dist/shims/compat-router.js +23 -0
  39. package/dist/shims/compat-router.js.map +1 -0
  40. package/dist/shims/font-google.generated.d.ts.map +1 -1
  41. package/dist/shims/font-google.generated.js +1923 -1923
  42. package/dist/shims/font-google.generated.js.map +1 -1
  43. package/dist/shims/image.d.ts.map +1 -1
  44. package/dist/shims/image.js +16 -3
  45. package/dist/shims/image.js.map +1 -1
  46. package/dist/shims/link.d.ts.map +1 -1
  47. package/dist/shims/link.js +15 -13
  48. package/dist/shims/link.js.map +1 -1
  49. package/dist/shims/metadata.d.ts.map +1 -1
  50. package/dist/shims/metadata.js +15 -4
  51. package/dist/shims/metadata.js.map +1 -1
  52. package/dist/shims/navigation.d.ts +1 -0
  53. package/dist/shims/navigation.d.ts.map +1 -1
  54. package/dist/shims/navigation.js +16 -0
  55. package/dist/shims/navigation.js.map +1 -1
  56. package/dist/shims/router.d.ts +18 -0
  57. package/dist/shims/router.d.ts.map +1 -1
  58. package/dist/shims/router.js +63 -30
  59. package/dist/shims/router.js.map +1 -1
  60. package/dist/shims/script.d.ts.map +1 -1
  61. package/dist/shims/script.js +18 -18
  62. package/dist/shims/script.js.map +1 -1
  63. package/dist/shims/server.d.ts +11 -2
  64. package/dist/shims/server.d.ts.map +1 -1
  65. package/dist/shims/server.js +11 -2
  66. package/dist/shims/server.js.map +1 -1
  67. package/dist/utils/project.d.ts +12 -6
  68. package/dist/utils/project.d.ts.map +1 -1
  69. package/dist/utils/project.js +56 -21
  70. package/dist/utils/project.js.map +1 -1
  71. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { parseAst } from "vite";
1
+ import { loadEnv, parseAst } from "vite";
2
2
  import { pagesRouter, apiRouter, invalidateRouteCache, matchRoute, patternToNextFormat as pagesPatternToNextFormat } from "./routing/pages-router.js";
3
3
  import { appRouter, invalidateAppRouteCache } from "./routing/app-router.js";
4
4
  import { createSSRHandler } from "./server/dev-server.js";
@@ -12,6 +12,7 @@ import { findInstrumentationFile, runInstrumentation } from "./server/instrument
12
12
  import { validateDevRequest } from "./server/dev-origin-check.js";
13
13
  import { safeRegExp, isExternalUrl, proxyExternalRequest, parseCookies, matchHeaders, matchRedirect, matchRewrite, } from "./config/config-matchers.js";
14
14
  import { scanMetadataFiles } from "./server/metadata-routes.js";
15
+ import { detectPackageManager } from "./utils/project.js";
15
16
  import tsconfigPaths from "vite-tsconfig-paths";
16
17
  import MagicString from "magic-string";
17
18
  import path from "node:path";
@@ -554,6 +555,13 @@ export default function vinext(options = {}) {
554
555
  rewrites: nextConfig?.rewrites ?? { beforeFiles: [], afterFiles: [], fallback: [] },
555
556
  headers: nextConfig?.headers ?? [],
556
557
  i18n: nextConfig?.i18n ?? null,
558
+ images: {
559
+ deviceSizes: nextConfig?.images?.deviceSizes,
560
+ imageSizes: nextConfig?.images?.imageSizes,
561
+ dangerouslyAllowSVG: nextConfig?.images?.dangerouslyAllowSVG,
562
+ contentDispositionType: nextConfig?.images?.contentDispositionType,
563
+ contentSecurityPolicy: nextConfig?.images?.contentSecurityPolicy,
564
+ },
557
565
  });
558
566
  // Generate middleware code if middleware.ts exists
559
567
  const middlewareImportCode = middlewarePath
@@ -656,7 +664,7 @@ import React from "react";
656
664
  import { renderToReadableStream } from "react-dom/server.edge";
657
665
  import { resetSSRHead, getSSRHeadHTML } from "next/head";
658
666
  import { flushPreloads } from "next/dynamic";
659
- import { setSSRContext } from "next/router";
667
+ import { setSSRContext, wrapWithRouterContext } from "next/router";
660
668
  import { getCacheHandler } from "next/cache";
661
669
  import { runWithFetchCache } from "vinext/fetch-cache";
662
670
  import { _runWithCacheState } from "next/cache";
@@ -1221,6 +1229,7 @@ export async function renderPage(request, url, manifest) {
1221
1229
  } else {
1222
1230
  element = React.createElement(PageComponent, pageProps);
1223
1231
  }
1232
+ element = wrapWithRouterContext(element);
1224
1233
 
1225
1234
  if (typeof resetSSRHead === "function") resetSSRHead();
1226
1235
  if (typeof flushPreloads === "function") await flushPreloads();
@@ -1318,6 +1327,7 @@ export async function renderPage(request, url, manifest) {
1318
1327
  } else {
1319
1328
  isrElement = React.createElement(PageComponent, pageProps);
1320
1329
  }
1330
+ isrElement = wrapWithRouterContext(isrElement);
1321
1331
  var isrHtml = await renderToStringAsync(isrElement);
1322
1332
  var fullHtml = shellPrefix + isrHtml + shellSuffix;
1323
1333
  var isrPathname = url.split("?")[0];
@@ -1479,6 +1489,10 @@ async function hydrate() {
1479
1489
  element = React.createElement(PageComponent, pageProps);
1480
1490
  `}
1481
1491
 
1492
+ // Wrap with RouterContext.Provider so next/compat/router works during hydration
1493
+ const { wrapWithRouterContext } = await import("next/router");
1494
+ element = wrapWithRouterContext(element);
1495
+
1482
1496
  const container = document.getElementById("__next");
1483
1497
  if (!container) {
1484
1498
  console.error("[vinext] No #__next element found");
@@ -1529,7 +1543,7 @@ hydrate();
1529
1543
  if (earlyAppDirExists && autoRsc) {
1530
1544
  if (!resolvedRscPath) {
1531
1545
  throw new Error("vinext: App Router detected but @vitejs/plugin-rsc is not installed.\n" +
1532
- "Run: npm install -D @vitejs/plugin-rsc");
1546
+ "Run: " + detectPackageManager(process.cwd()) + " @vitejs/plugin-rsc");
1533
1547
  }
1534
1548
  const rscImport = import(pathToFileURL(resolvedRscPath).href);
1535
1549
  rscPluginPromise = rscImport
@@ -1545,6 +1559,9 @@ hydrate();
1545
1559
  });
1546
1560
  }
1547
1561
  const imageImportDimCache = new Map();
1562
+ // Shared state for the MDX proxy plugin. Populated during config() if MDX
1563
+ // files are detected and @mdx-js/rollup is installed.
1564
+ let mdxDelegate = null;
1548
1565
  const plugins = [
1549
1566
  // Resolve tsconfig paths/baseUrl aliases so real-world Next.js repos
1550
1567
  // that use @/*, #/*, or baseUrl imports work out of the box.
@@ -1555,8 +1572,36 @@ hydrate();
1555
1572
  {
1556
1573
  name: "vinext:config",
1557
1574
  enforce: "pre",
1558
- async config(config) {
1575
+ async config(config, env) {
1559
1576
  root = config.root ?? process.cwd();
1577
+ // Load .env files into process.env before anything else.
1578
+ // Next.js loads .env files before evaluating next.config.js, so
1579
+ // env vars are available in config, server-side code, and as
1580
+ // NEXT_PUBLIC_* defines for the client bundle.
1581
+ // Pass '' as prefix to load ALL vars, not just VITE_-prefixed ones.
1582
+ const mode = env?.mode ?? "development";
1583
+ const envDir = config.envDir ?? root;
1584
+ const dotenvVars = loadEnv(mode, envDir, "");
1585
+ for (const [key, value] of Object.entries(dotenvVars)) {
1586
+ if (process.env[key] === undefined) {
1587
+ process.env[key] = value;
1588
+ }
1589
+ }
1590
+ // Align NODE_ENV with Next.js semantics: build -> production, serve -> development.
1591
+ // Next.js unconditionally forces NODE_ENV during build/dev, so we do the same.
1592
+ let resolvedNodeEnv;
1593
+ if (mode === "test") {
1594
+ resolvedNodeEnv = "test";
1595
+ }
1596
+ else if (env?.command === "build") {
1597
+ resolvedNodeEnv = "production";
1598
+ }
1599
+ else {
1600
+ resolvedNodeEnv = "development";
1601
+ }
1602
+ if (process.env.NODE_ENV !== resolvedNodeEnv) {
1603
+ process.env.NODE_ENV = resolvedNodeEnv;
1604
+ }
1560
1605
  // Resolve the base directory for app/pages detection.
1561
1606
  // If appDir is provided, resolve it (supports both relative and absolute paths).
1562
1607
  // If not provided, auto-detect: check root first, then src/ subdirectory.
@@ -1593,7 +1638,16 @@ hydrate();
1593
1638
  nextConfig = await resolveNextConfig(rawConfig);
1594
1639
  // Merge env from next.config.js with NEXT_PUBLIC_* env vars
1595
1640
  const defines = getNextPublicEnvDefines();
1641
+ if (!config.define ||
1642
+ typeof config.define !== "object" ||
1643
+ !("process.env.NODE_ENV" in config.define)) {
1644
+ defines["process.env.NODE_ENV"] = JSON.stringify(resolvedNodeEnv);
1645
+ }
1596
1646
  for (const [key, value] of Object.entries(nextConfig.env)) {
1647
+ // Skip NODE_ENV from next.config.js env — Next.js ignores it too,
1648
+ // and it would silently override the value we just set above.
1649
+ if (key === "NODE_ENV")
1650
+ continue;
1597
1651
  defines[`process.env.${key}`] = JSON.stringify(value);
1598
1652
  }
1599
1653
  // Expose basePath to client-side code
@@ -1610,6 +1664,9 @@ hydrate();
1610
1664
  defines["process.env.__VINEXT_IMAGE_DEVICE_SIZES"] = JSON.stringify(JSON.stringify(deviceSizes));
1611
1665
  defines["process.env.__VINEXT_IMAGE_SIZES"] = JSON.stringify(JSON.stringify(imageSizes));
1612
1666
  }
1667
+ // Expose dangerouslyAllowSVG flag for the image shim's auto-skip logic.
1668
+ // When false (default), .svg sources bypass the optimization endpoint.
1669
+ defines["process.env.__VINEXT_IMAGE_DANGEROUSLY_ALLOW_SVG"] = JSON.stringify(String(nextConfig.images?.dangerouslyAllowSVG ?? false));
1613
1670
  // Draft mode secret — generated once at build time so the
1614
1671
  // __prerender_bypass cookie is consistent across all server
1615
1672
  // instances (e.g. multiple Cloudflare Workers isolates).
@@ -1620,6 +1677,7 @@ hydrate();
1620
1677
  "next/link": path.join(shimsDir, "link"),
1621
1678
  "next/head": path.join(shimsDir, "head"),
1622
1679
  "next/router": path.join(shimsDir, "router"),
1680
+ "next/compat/router": path.join(shimsDir, "compat-router"),
1623
1681
  "next/image": path.join(shimsDir, "image"),
1624
1682
  "next/legacy/image": path.join(shimsDir, "legacy-image"),
1625
1683
  "next/dynamic": path.join(shimsDir, "dynamic"),
@@ -1698,11 +1756,10 @@ hydrate();
1698
1756
  // already configured. Applies remark/rehype plugins from next.config.
1699
1757
  const hasMdxPlugin = pluginsFlat.some((p) => p && typeof p === "object" && typeof p.name === "string" &&
1700
1758
  (p.name === "@mdx-js/rollup" || p.name === "mdx"));
1701
- const mdxPlugins = [];
1702
1759
  if (!hasMdxPlugin && hasMdxFiles(root, hasAppDir ? appDir : null, hasPagesDir ? pagesDir : null)) {
1703
1760
  try {
1704
1761
  const mdxRollup = await import("@mdx-js/rollup");
1705
- const mdxPlugin = mdxRollup.default ?? mdxRollup;
1762
+ const mdxFactory = mdxRollup.default ?? mdxRollup;
1706
1763
  const mdxOpts = {};
1707
1764
  if (nextConfig.mdx) {
1708
1765
  if (nextConfig.mdx.remarkPlugins)
@@ -1712,7 +1769,7 @@ hydrate();
1712
1769
  if (nextConfig.mdx.recmaPlugins)
1713
1770
  mdxOpts.recmaPlugins = nextConfig.mdx.recmaPlugins;
1714
1771
  }
1715
- mdxPlugins.push(mdxPlugin(mdxOpts));
1772
+ mdxDelegate = mdxFactory(mdxOpts);
1716
1773
  if (nextConfig.mdx) {
1717
1774
  console.log("[vinext] Auto-injected @mdx-js/rollup with remark/rehype plugins from next.config");
1718
1775
  }
@@ -1723,7 +1780,7 @@ hydrate();
1723
1780
  catch {
1724
1781
  // @mdx-js/rollup not installed — warn but don't fail
1725
1782
  console.warn("[vinext] MDX files detected but @mdx-js/rollup is not installed. " +
1726
- "Install it with: npm install -D @mdx-js/rollup");
1783
+ "Install it with: " + detectPackageManager(process.cwd()) + " @mdx-js/rollup");
1727
1784
  }
1728
1785
  }
1729
1786
  // Detect if this is a standalone SSR build (set by `vite build --ssr`
@@ -1936,10 +1993,6 @@ hydrate();
1936
1993
  },
1937
1994
  };
1938
1995
  }
1939
- // Add auto-injected MDX plugin if needed
1940
- if (mdxPlugins.length > 0) {
1941
- viteConfig.plugins = mdxPlugins;
1942
- }
1943
1996
  return viteConfig;
1944
1997
  },
1945
1998
  configResolved(config) {
@@ -2045,6 +2098,31 @@ hydrate();
2045
2098
  }
2046
2099
  },
2047
2100
  },
2101
+ // Proxy plugin for @mdx-js/rollup. The real MDX plugin is created lazily
2102
+ // during vinext:config's config() (when MDX files are detected), but
2103
+ // plugins returned from config() hooks run too late in the pipeline —
2104
+ // after vite:import-analysis. This top-level proxy with enforce:"pre"
2105
+ // ensures MDX transforms run at the correct stage. Both vinext:config
2106
+ // and this proxy are enforce:"pre", and vinext:config comes first in
2107
+ // the array, so mdxDelegate is already set when this proxy's hooks fire.
2108
+ {
2109
+ name: "vinext:mdx",
2110
+ enforce: "pre",
2111
+ config(config, env) {
2112
+ if (!mdxDelegate?.config)
2113
+ return;
2114
+ const hook = mdxDelegate.config;
2115
+ const fn = typeof hook === "function" ? hook : hook.handler;
2116
+ return fn.call(this, config, env);
2117
+ },
2118
+ transform(code, id, options) {
2119
+ if (!mdxDelegate?.transform)
2120
+ return;
2121
+ const hook = mdxDelegate.transform;
2122
+ const fn = typeof hook === "function" ? hook : hook.handler;
2123
+ return fn.call(this, code, id, options);
2124
+ },
2125
+ },
2048
2126
  // Shim React canary/experimental APIs (ViewTransition, addTransitionType)
2049
2127
  // that exist in Next.js's bundled React canary but not in stable React 19.
2050
2128
  // Provides graceful no-op fallbacks so projects using these APIs degrade
@@ -2311,9 +2389,22 @@ hydrate();
2311
2389
  const result = await runMiddleware(server, middlewarePath, middlewareRequest);
2312
2390
  if (!result.continue) {
2313
2391
  if (result.redirectUrl) {
2314
- res.writeHead(result.redirectStatus ?? 307, {
2315
- Location: result.redirectUrl,
2316
- });
2392
+ const redirectHeaders = { Location: result.redirectUrl };
2393
+ if (result.responseHeaders) {
2394
+ for (const [key, value] of result.responseHeaders) {
2395
+ const existing = redirectHeaders[key];
2396
+ if (existing === undefined) {
2397
+ redirectHeaders[key] = value;
2398
+ }
2399
+ else if (Array.isArray(existing)) {
2400
+ existing.push(value);
2401
+ }
2402
+ else {
2403
+ redirectHeaders[key] = [existing, value];
2404
+ }
2405
+ }
2406
+ }
2407
+ res.writeHead(result.redirectStatus ?? 307, redirectHeaders);
2317
2408
  res.end();
2318
2409
  return;
2319
2410
  }
@@ -2383,7 +2474,10 @@ hydrate();
2383
2474
  const handled = await handleApiRoute(server, req, res, resolvedUrl, apiRoutes);
2384
2475
  if (handled)
2385
2476
  return;
2386
- // No API route matched — fall through to 404
2477
+ // No API route matched — if app dir exists, let the RSC plugin handle it
2478
+ // (app/api/* route handlers live there). Otherwise hard-404.
2479
+ if (hasAppDir)
2480
+ return next();
2387
2481
  res.statusCode = 404;
2388
2482
  res.end("404 - API route not found");
2389
2483
  return;
@@ -2424,7 +2518,10 @@ hydrate();
2424
2518
  return;
2425
2519
  }
2426
2520
  }
2427
- // No fallback matched render as-is (will hit 404 handler)
2521
+ // No fallback matched - if app dir exists, let the RSC plugin handle it,
2522
+ // otherwise render via the pages SSR handler (will 404 for unknown routes).
2523
+ if (hasAppDir)
2524
+ return next();
2428
2525
  await handler(req, res, resolvedUrl, mwStatus);
2429
2526
  }
2430
2527
  catch (e) {
@@ -2767,7 +2864,7 @@ hydrate();
2767
2864
  return null;
2768
2865
  if (!resolvedRscTransformsPath) {
2769
2866
  throw new Error("vinext: 'use cache' requires @vitejs/plugin-rsc to be installed.\n" +
2770
- "Run: npm install -D @vitejs/plugin-rsc");
2867
+ "Run: " + detectPackageManager(process.cwd()) + " @vitejs/plugin-rsc");
2771
2868
  }
2772
2869
  const { transformWrapExport, transformHoistInlineDirective } = await import(pathToFileURL(resolvedRscTransformsPath).href);
2773
2870
  const ast = parseAst(code);
@@ -2905,6 +3002,33 @@ hydrate();
2905
3002
  },
2906
3003
  },
2907
3004
  },
3005
+ // Write image config JSON for the App Router production server.
3006
+ // The App Router RSC entry doesn't export vinextConfig (that's a Pages
3007
+ // Router pattern), so we write a separate JSON file at build time that
3008
+ // prod-server.ts reads at startup for SVG/security header config.
3009
+ {
3010
+ name: "vinext:image-config",
3011
+ apply: "build",
3012
+ enforce: "post",
3013
+ writeBundle: {
3014
+ sequential: true,
3015
+ order: "post",
3016
+ handler(options) {
3017
+ const envName = this.environment?.name;
3018
+ if (envName !== "rsc")
3019
+ return;
3020
+ const outDir = options.dir;
3021
+ if (!outDir)
3022
+ return;
3023
+ const imageConfig = {
3024
+ dangerouslyAllowSVG: nextConfig?.images?.dangerouslyAllowSVG,
3025
+ contentDispositionType: nextConfig?.images?.contentDispositionType,
3026
+ contentSecurityPolicy: nextConfig?.images?.contentSecurityPolicy,
3027
+ };
3028
+ fs.writeFileSync(path.join(outDir, "image-config.json"), JSON.stringify(imageConfig));
3029
+ },
3030
+ },
3031
+ },
2908
3032
  // Cloudflare Workers production build integration:
2909
3033
  // After all environments are built, compute lazy chunks from the client
2910
3034
  // build manifest and inject globals into the worker entry.