@qzsy/vinext 0.1.8 → 0.1.10

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.
package/dist/cli.js CHANGED
File without changes
package/dist/index.js CHANGED
@@ -341,6 +341,22 @@ const _fontGoogleShimPath = resolveShimModulePath(_shimsDir, "font-google");
341
341
  const _appBrowserServerActionClientPath = resolveShimModulePath(normalizePathSeparators(path.resolve(__dirname, "server")), "app-browser-server-action-client");
342
342
  const _appRscHandlerPath = resolveShimModulePath(normalizePathSeparators(path.resolve(__dirname, "server")), "app-rsc-handler");
343
343
  const _canExternalizeAppRscHandler = _appRscHandlerPath.endsWith(".js");
344
+ const _defaultImageLoaderShimPath = resolveShimModulePath(_shimsDir, "default-image-loader");
345
+ /** Absolute path to `images.loaderFile` when configured; set in vinext:config. */
346
+ let _imageLoaderFile;
347
+ function isImageShimModule(importer) {
348
+ if (!importer) return false;
349
+ const normalized = normalizePathSeparators(importer.split("?")[0]);
350
+ return /\/shims\/image\.(tsx?|jsx?)$/.test(normalized);
351
+ }
352
+ function isDefaultImageLoaderRequest(id, importer) {
353
+ const cleanId = id.startsWith("\0") ? id.slice(1) : id;
354
+ const normalizedId = normalizePathSeparators(cleanId.split("?")[0]);
355
+ if (normalizedId === normalizePathSeparators(_defaultImageLoaderShimPath)) return true;
356
+ if (/\/shims\/default-image-loader\.(tsx?|jsx?)$/.test(normalizedId)) return true;
357
+ if (isImageShimModule(importer) && (cleanId === "./default-image-loader.js" || cleanId === "./default-image-loader" || cleanId === "./default-image-loader.ts" || cleanId === "./default-image-loader.tsx")) return true;
358
+ return false;
359
+ }
344
360
  function isValidExportIdentifier(name) {
345
361
  return /^[$A-Z_a-z][$\w]*$/.test(name);
346
362
  }
@@ -623,6 +639,16 @@ function vinext(options = {}) {
623
639
  serverOnlyShimPath: resolveShimModulePath(shimsDir, "server-only")
624
640
  }),
625
641
  dataUrlCssPlugin(),
642
+ {
643
+ name: "vinext:image-loader-file",
644
+ enforce: "pre",
645
+ resolveId: {
646
+ filter: { id: /default-image-loader/ },
647
+ handler(id, importer) {
648
+ if (_imageLoaderFile && isDefaultImageLoaderRequest(id, importer)) return _imageLoaderFile;
649
+ }
650
+ }
651
+ },
626
652
  {
627
653
  name: "vinext:config",
628
654
  enforce: "pre",
@@ -731,6 +757,7 @@ function vinext(options = {}) {
731
757
  }
732
758
  defines["process.env.__VINEXT_IMAGE_DANGEROUSLY_ALLOW_SVG"] = JSON.stringify(String(nextConfig.images?.dangerouslyAllowSVG ?? false));
733
759
  defines["process.env.__VINEXT_IMAGE_DANGEROUSLY_ALLOW_LOCAL_IP"] = JSON.stringify(String(nextConfig.images?.dangerouslyAllowLocalIP ?? false));
760
+ defines["process.env.__VINEXT_IMAGE_LOADER_CONFIGURED"] = JSON.stringify(nextConfig.images?.loaderFile ? "true" : "false");
734
761
  defines["process.env.__VINEXT_BUILD_ID"] = JSON.stringify(nextConfig.buildId);
735
762
  defines["process.env.__VINEXT_RSC_COMPATIBILITY_ID"] = JSON.stringify(rscCompatibilityId);
736
763
  defines["process.env.__VINEXT_DEPLOYMENT_ID"] = JSON.stringify(nextConfig.deploymentId ?? "");
@@ -826,6 +853,7 @@ function vinext(options = {}) {
826
853
  overlay: false
827
854
  };
828
855
  const cssModulesOverride = config.css?.modules === false || typeof config.css?.modules === "object" && "Loader" in config.css.modules ? {} : { modules: { Loader: sassComposesLoader.Loader } };
856
+ _imageLoaderFile = nextConfig.images?.loaderFile;
829
857
  const viteConfig = {
830
858
  appType: "custom",
831
859
  build: {
@@ -917,9 +945,10 @@ function vinext(options = {}) {
917
945
  const incomingInclude = config.optimizeDeps?.include ?? [];
918
946
  const depOptimizeAliasPlugin = {
919
947
  name: "vinext:dep-optimize-alias",
920
- resolveId(id) {
948
+ resolveId(id, importer) {
921
949
  const shimBase = _reactServerShims.get(id);
922
950
  if (shimBase !== void 0) return resolveShimModulePath(shimsDir, shimBase);
951
+ if (_imageLoaderFile && isDefaultImageLoaderRequest(id, importer)) return _imageLoaderFile;
923
952
  }
924
953
  };
925
954
  const depOptimizeNodeEnvOptions = getDepOptimizeNodeEnvOptions(viteMajorVersion, nodeEnvDefine);
@@ -17,8 +17,8 @@ import "./app-page-response.js";
17
17
  import { buildNextDataNotFoundResponse, normalizePagesDataRequest } from "./pages-data-route.js";
18
18
  import { matchPrerenderRouteParamsPayload, readTrustedPrerenderRouteParams, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
19
19
  import { getRenderedConcreteUrlPathsForRoute } from "./pregenerated-concrete-paths.js";
20
- import { pickRootParams, setRootParams } from "../shims/root-params.js";
21
20
  import { flattenErrorCauses } from "../utils/error-cause.js";
21
+ import { pickRootParams, setRootParams } from "../shims/root-params.js";
22
22
  import { createServerActionNotFoundResponse, getServerActionNotFoundMessage } from "./server-action-not-found.js";
23
23
  import { buildPageCacheTags } from "./implicit-tags.js";
24
24
  import { buildPostMwRequestContext } from "./app-post-middleware-context.js";
@@ -0,0 +1,12 @@
1
+ //#region src/shims/default-image-loader.d.ts
2
+ declare function defaultImageLoader({
3
+ src,
4
+ width,
5
+ quality
6
+ }: {
7
+ src: string;
8
+ width: number;
9
+ quality?: number;
10
+ }): string;
11
+ //#endregion
12
+ export { defaultImageLoader as default };
@@ -0,0 +1,13 @@
1
+ import { imageOptimizationUrl } from "./image-optimization-url.js";
2
+ //#region src/shims/default-image-loader.ts
3
+ /**
4
+ * Default image loader for next/image.
5
+ *
6
+ * When `images.loaderFile` is configured in next.config.js, vinext aliases
7
+ * this module to the user's custom loader at build time (mirroring Next.js).
8
+ */
9
+ function defaultImageLoader({ src, width, quality = 75 }) {
10
+ return imageOptimizationUrl(src, width, quality);
11
+ }
12
+ //#endregion
13
+ export { defaultImageLoader as default };
@@ -0,0 +1,10 @@
1
+ //#region src/shims/image-optimization-url.d.ts
2
+ /**
3
+ * Build a `/_next/image` optimization URL.
4
+ *
5
+ * Shared by the next/image shim and the default image loader module so
6
+ * `images.loaderFile` aliasing does not create a circular import.
7
+ */
8
+ declare function imageOptimizationUrl(src: string, width: number, quality?: number): string;
9
+ //#endregion
10
+ export { imageOptimizationUrl };
@@ -0,0 +1,12 @@
1
+ //#region src/shims/image-optimization-url.ts
2
+ /**
3
+ * Build a `/_next/image` optimization URL.
4
+ *
5
+ * Shared by the next/image shim and the default image loader module so
6
+ * `images.loaderFile` aliasing does not create a circular import.
7
+ */
8
+ function imageOptimizationUrl(src, width, quality = 75) {
9
+ return `/_next/image?url=${encodeURIComponent(src)}&w=${width}&q=${quality}`;
10
+ }
11
+ //#endregion
12
+ export { imageOptimizationUrl };
@@ -1,3 +1,4 @@
1
+ import { imageOptimizationUrl } from "./image-optimization-url.js";
1
2
  import React from "react";
2
3
 
3
4
  //#region src/shims/image.d.ts
@@ -35,14 +36,6 @@ type ImageProps = {
35
36
  overrideSrc?: string;
36
37
  loading?: "lazy" | "eager";
37
38
  };
38
- /**
39
- * Build a `/_next/image` optimization URL.
40
- *
41
- * In production (Cloudflare Workers), the worker intercepts this path and uses
42
- * the Images binding to resize/transcode on the fly. In dev, the Vite dev
43
- * server handles it as a passthrough (serves the original file).
44
- */
45
- declare function imageOptimizationUrl(src: string, width: number, quality?: number): string;
46
39
  declare const Image: React.ForwardRefExoticComponent<ImageProps & React.RefAttributes<HTMLImageElement>>;
47
40
  /**
48
41
  * getImageProps — for advanced use cases (picture elements, background images).
@@ -1,4 +1,6 @@
1
1
  "use client";
2
+ import { imageOptimizationUrl } from "./image-optimization-url.js";
3
+ import defaultImageLoader from "./default-image-loader.js";
2
4
  import { useMergedRef } from "./use-merged-ref.js";
3
5
  import { hasRemoteMatch, isPrivateIp } from "./image-config.js";
4
6
  import { forwardRef, useEffect, useLayoutEffect, useRef, useState } from "react";
@@ -68,6 +70,14 @@ const __dangerouslyAllowSVG = process.env.__VINEXT_IMAGE_DANGEROUSLY_ALLOW_SVG =
68
70
  * are blocked to mitigate SSRF risk.
69
71
  */
70
72
  const __dangerouslyAllowLocalIP = process.env.__VINEXT_IMAGE_DANGEROUSLY_ALLOW_LOCAL_IP === "true";
73
+ /** Set when next.config.js configures images.loaderFile. */
74
+ const __imageLoaderConfigured = process.env.__VINEXT_IMAGE_LOADER_CONFIGURED === "true";
75
+ function resolveEffectiveLoader(loader) {
76
+ if (loader) return loader;
77
+ if (!__imageLoaderConfigured) return void 0;
78
+ if (typeof defaultImageLoader !== "function") throw new Error("images.loaderFile detected but the file is missing default export.\nRead more: https://nextjs.org/docs/messages/invalid-images-config");
79
+ return defaultImageLoader;
80
+ }
71
81
  /**
72
82
  * Validate that a remote URL is allowed by the configured remote patterns.
73
83
  * Returns true if the URL is allowed, false otherwise.
@@ -211,16 +221,6 @@ function resolveImageSource(v) {
211
221
  * Configurable via `images.deviceSizes` in next.config.js.
212
222
  */
213
223
  const RESPONSIVE_WIDTHS = __imageDeviceSizes;
214
- /**
215
- * Build a `/_next/image` optimization URL.
216
- *
217
- * In production (Cloudflare Workers), the worker intercepts this path and uses
218
- * the Images binding to resize/transcode on the fly. In dev, the Vite dev
219
- * server handles it as a passthrough (serves the original file).
220
- */
221
- function imageOptimizationUrl(src, width, quality = 75) {
222
- return `/_next/image?url=${encodeURIComponent(src)}&w=${width}&q=${quality}`;
223
- }
224
224
  function preloadImageResource(input) {
225
225
  if (!input.shouldPreload) return;
226
226
  if (typeof ReactDOM.preload !== "function") return;
@@ -270,6 +270,7 @@ const Image = forwardRef(function Image({ src: srcProp, alt, width, height, fill
270
270
  const shouldPreload = preload === true || priority === true;
271
271
  const priorityFetchPriority = priority ? "high" : void 0;
272
272
  const imageLoading = priority ? "eager" : shouldPreload ? loading : loading ?? "lazy";
273
+ const effectiveLoader = resolveEffectiveLoader(loader);
273
274
  const [completedBlurSrc, setCompletedBlurSrc] = useState(void 0);
274
275
  const blurComplete = completedBlurSrc === src;
275
276
  const markBlurComplete = () => {
@@ -360,8 +361,8 @@ const Image = forwardRef(function Image({ src: srcProp, alt, width, height, fill
360
361
  ...rest
361
362
  });
362
363
  }
363
- if (loader) {
364
- const resolvedSrc = loader({
364
+ if (effectiveLoader) {
365
+ const resolvedSrc = effectiveLoader({
365
366
  src,
366
367
  width: imgWidth ?? 0,
367
368
  quality: quality ?? 75
@@ -508,6 +509,7 @@ function getImageProps(props) {
508
509
  blurDataURL: blurDataURLProp
509
510
  });
510
511
  const shouldPreload = _preload === true || priority === true;
512
+ const effectiveLoader = resolveEffectiveLoader(loader);
511
513
  if (_unoptimized === true) {
512
514
  const renderedSrc = overrideSrc || src;
513
515
  const sanitizedBlurURL = imgBlurDataURL ? sanitizeBlurDataURL(imgBlurDataURL) : void 0;
@@ -544,14 +546,14 @@ function getImageProps(props) {
544
546
  }
545
547
  }
546
548
  const imgQuality = _quality ?? 75;
547
- const resolvedSrc = blockedInProd ? "" : loader ? loader({
549
+ const resolvedSrc = blockedInProd ? "" : effectiveLoader ? effectiveLoader({
548
550
  src,
549
551
  width: imgWidth ?? 0,
550
552
  quality: imgQuality
551
553
  }) : src;
552
- const skipOpt = isSvgUrl(resolvedSrc) && !__dangerouslyAllowSVG || blockedInProd || !!loader || isRemoteUrl(resolvedSrc);
554
+ const skipOpt = isSvgUrl(resolvedSrc) && !__dangerouslyAllowSVG || blockedInProd || !!effectiveLoader || isRemoteUrl(resolvedSrc);
553
555
  const optimizedSrc = skipOpt ? resolvedSrc : imgWidth ? imageOptimizationUrl(resolvedSrc, imgWidth, imgQuality) : imageOptimizationUrl(resolvedSrc, RESPONSIVE_WIDTHS[0], imgQuality);
554
- const srcSet = imgWidth && !fill && !isRemoteUrl(resolvedSrc) && !loader && !skipOpt ? generateSrcSet(resolvedSrc, imgWidth, imgQuality) : void 0;
556
+ const srcSet = imgWidth && !fill && !isRemoteUrl(resolvedSrc) && !effectiveLoader && !skipOpt ? generateSrcSet(resolvedSrc, imgWidth, imgQuality) : void 0;
555
557
  const sanitizedBlurURL = imgBlurDataURL ? sanitizeBlurDataURL(imgBlurDataURL) : void 0;
556
558
  const blurStyle = placeholder === "blur" && sanitizedBlurURL ? {
557
559
  backgroundImage: `url(${sanitizedBlurURL})`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qzsy/vinext",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Run Next.js apps on Vite. Drop-in replacement for the next CLI.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -89,26 +89,31 @@
89
89
  "publishConfig": {
90
90
  "access": "public"
91
91
  },
92
+ "scripts": {
93
+ "build": "vp pack",
94
+ "prepack": "cp ../../README.md README.md && vp run build",
95
+ "dev": "vp pack --watch"
96
+ },
92
97
  "dependencies": {
93
- "@unpic/react": "^1.0.2",
94
- "@vercel/og": "^0.8.6",
95
- "image-size": "2.0.2",
96
- "ipaddr.js": "^2.1.0",
97
- "magic-string": "^0.30.21",
98
- "vite-plugin-commonjs": "^0.10.4",
99
- "web-vitals": "^4.2.4"
98
+ "@unpic/react": "catalog:",
99
+ "@vercel/og": "catalog:",
100
+ "image-size": "catalog:",
101
+ "ipaddr.js": "catalog:",
102
+ "magic-string": "catalog:",
103
+ "vite-plugin-commonjs": "catalog:",
104
+ "web-vitals": "catalog:"
100
105
  },
101
106
  "devDependencies": {
102
- "@types/node": "^25.9.2",
103
- "@types/react": "^19.2.16",
104
- "@types/react-dom": "^19.2.3",
105
- "@vitejs/plugin-react": "^6.0.1",
106
- "@vitejs/plugin-rsc": "^0.5.27",
107
- "react-server-dom-webpack": "^19.2.7",
108
- "vite": "npm:@voidzero-dev/vite-plus-core@0.2.1",
109
- "vite-plus": "0.2.1",
110
- "vite-tsconfig-paths": "^6.1.1",
111
- "@vinext/cloudflare": "0.1.2"
107
+ "@types/node": "catalog:",
108
+ "@types/react": "catalog:",
109
+ "@types/react-dom": "catalog:",
110
+ "@vinext/cloudflare": "workspace:*",
111
+ "@vitejs/plugin-react": "catalog:",
112
+ "@vitejs/plugin-rsc": "catalog:",
113
+ "react-server-dom-webpack": "catalog:",
114
+ "vite": "catalog:",
115
+ "vite-plus": "catalog:",
116
+ "vite-tsconfig-paths": "catalog:"
112
117
  },
113
118
  "peerDependencies": {
114
119
  "@mdx-js/rollup": "^3.0.0",
@@ -136,9 +141,5 @@
136
141
  },
137
142
  "engines": {
138
143
  "node": ">=22"
139
- },
140
- "scripts": {
141
- "build": "vp pack",
142
- "dev": "vp pack --watch"
143
144
  }
144
- }
145
+ }