@son426/vite-image 0.1.1 → 0.1.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.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @son426/vite-image
2
2
 
3
- **Next.js 수준의 Image 컴포넌트를 Vite에서 제공합니다.**
3
+ **Next.js-style Image component for Vite.**
4
4
 
5
- 정적 번들러인 Vite에서 Next.js 유사한 스펙의 Image 컴포넌트를 구현한 것이 라이브러리의 핵심 철학입니다. `vite.config.ts`에 `viteImage()`를 추가하고, `<Image />` 컴포넌트를 바로 활용할 수 있습니다.
5
+ This library provides a Next.js-like Image component experience in Vite. The core philosophy is to bring static bundler capabilities similar to Next.js Image component to Vite projects. Simply add `viteImage()` to your `vite.config.ts` and start using the `<Image />` component.
6
6
 
7
7
  ## Features
8
8
 
@@ -10,7 +10,7 @@
10
10
  - 🎨 **LQIP Support**: Low Quality Image Placeholder for smooth loading experience
11
11
  - 📱 **Responsive Images**: Built-in support for srcSet and sizes attributes
12
12
  - 🎯 **TypeScript**: Full TypeScript support with type definitions
13
- - 🚀 **Zero Config**: Works out of the box with sensible defaults
13
+ - 🚀 **Simple Setup**: Easy configuration with sensible defaults
14
14
 
15
15
  ## Installation
16
16
 
@@ -24,7 +24,7 @@ yarn add @son426/vite-image
24
24
 
25
25
  ## Peer Dependencies
26
26
 
27
- 다음 패키지들이 필요합니다:
27
+ The following packages are required:
28
28
 
29
29
  ```bash
30
30
  pnpm add react vite vite-imagetools
@@ -34,7 +34,7 @@ pnpm add react vite vite-imagetools
34
34
 
35
35
  ### 1. Setup Vite Plugin
36
36
 
37
- `vite.config.ts`에 플러그인을 추가하세요:
37
+ Add the plugin to your `vite.config.ts`:
38
38
 
39
39
  ```typescript
40
40
  import { defineConfig } from "vite";
@@ -42,37 +42,28 @@ import { viteImage } from "@son426/vite-image/plugin";
42
42
 
43
43
  export default defineConfig({
44
44
  plugins: [
45
- viteImage({
46
- // Optional: vite-imagetools options
47
- defaultDirectives: (url) => {
48
- if (url.searchParams.has("webp")) {
49
- return new URLSearchParams("format=webp");
50
- }
51
- return new URLSearchParams();
52
- },
53
- }),
45
+ // ... other plugins
46
+ viteImage(),
54
47
  ],
55
48
  });
56
49
  ```
57
50
 
51
+ **Important**: The `viteImage()` function returns an array of plugins, so you should spread it when adding to the plugins array.
52
+
58
53
  ### 2. Use the Component
59
54
 
60
- ```typescript
61
- import { Image } from "@son426/vite-image/react";
55
+ #### Using `?vite-image` query (Required)
62
56
 
63
- // Import optimized images
64
- import imageSrcSet from "@/assets/image.webp?w=640;1024;1920&format=webp&as=srcset";
65
- import imageMeta from "@/assets/image.webp?w=1920&format=webp&as=meta";
66
- import imageLqipSrc from "@/assets/image.webp?w=20&blur=2&quality=20&format=webp&inline";
57
+ The `?vite-image` query parameter is required and automatically generates all required image data. When using `?vite-image`, the `src` prop must be an object (not a string).
58
+
59
+ ```typescript
60
+ import Image from "@son426/vite-image/react";
61
+ import bgImage from "@/assets/image.webp?vite-image";
67
62
 
68
63
  function MyComponent() {
69
64
  return (
70
65
  <Image
71
- src={imageMeta.src}
72
- srcSet={imageSrcSet}
73
- lqipSrc={imageLqipSrc}
74
- width={imageMeta.width}
75
- height={imageMeta.height}
66
+ src={bgImage}
76
67
  fill={false}
77
68
  sizes="(max-width: 640px) 100vw, (max-width: 1024px) 100vw, 1920px"
78
69
  alt="Description"
@@ -81,20 +72,22 @@ function MyComponent() {
81
72
  }
82
73
  ```
83
74
 
75
+ **Important**: When using `?vite-image`, the `src` prop must receive the imported object directly. String URLs are not supported for `?vite-image` imports.
76
+
77
+ The `?vite-image` query automatically generates:
78
+
79
+ - `src`: Optimized image URL
80
+ - `srcSet`: Responsive srcSet string
81
+ - `lqipSrc`: Low Quality Image Placeholder (base64 inline)
82
+ - `width` and `height`: Image dimensions
83
+
84
84
  ### Fill Mode
85
85
 
86
- 컨테이너를 채우는 이미지 (Next.js Image와 유사):
86
+ For images that fill their container (similar to Next.js Image):
87
87
 
88
88
  ```typescript
89
89
  <div style={{ position: "relative", width: "100%", height: "400px" }}>
90
- <Image
91
- src={imageMeta.src}
92
- srcSet={imageSrcSet}
93
- lqipSrc={imageLqipSrc}
94
- fill={true}
95
- sizes="100vw"
96
- alt="Description"
97
- />
90
+ <Image src={bgImage} fill={true} sizes="100vw" alt="Description" />
98
91
  </div>
99
92
  ```
100
93
 
@@ -102,29 +95,54 @@ function MyComponent() {
102
95
 
103
96
  ### Image Props
104
97
 
105
- | Prop | Type | Required | Description |
106
- | --------- | ----------------------- | -------- | ---------------------------------------------- |
107
- | `src` | `string` | Yes | Image source URL |
108
- | `srcSet` | `string` | No | Responsive image srcSet |
109
- | `lqipSrc` | `string` | No | Low Quality Image Placeholder source |
110
- | `fill` | `boolean` | No | Fill container mode (default: `false`) |
111
- | `width` | `number` | Yes* | Image width (required when `fill={false}`) |
112
- | `height` | `number` | Yes* | Image height (required when `fill={false}`) |
113
- | `sizes` | `string` | No | Sizes attribute (default: `"100vw"`) |
114
- | `className` | `string` | No | Additional CSS classes |
115
- | `...props`| `ImgHTMLAttributes` | No | All standard img element attributes |
98
+ | Prop | Type | Required | Description |
99
+ | ----------- | --------------------- | -------- | ------------------------------------------ |
100
+ | `src` | `ResponsiveImageData` | Yes | Image data object from `?vite-image` query |
101
+ | `fill` | `boolean` | No | Fill container mode (default: `false`) |
102
+ | `sizes` | `string` | No | Sizes attribute (default: `"100vw"`) |
103
+ | `className` | `string` | No | Additional CSS classes |
104
+ | `style` | `CSSProperties` | No | Additional inline styles |
105
+ | `...props` | `ImgHTMLAttributes` | No | All standard img element attributes |
106
+
107
+ **Note**: The `src` prop must be an object imported from `?vite-image` query. String URLs are not supported. The `width` and `height` are automatically extracted from the `src` object.
108
+
109
+ ### ResponsiveImageData
116
110
 
117
- \* Required when `fill={false}`
111
+ The type returned from `?vite-image` query:
112
+
113
+ ```typescript
114
+ interface ResponsiveImageData {
115
+ src: string;
116
+ width: number;
117
+ height: number;
118
+ srcSet?: string;
119
+ lqipSrc?: string;
120
+ }
121
+ ```
118
122
 
119
123
  ## TypeScript
120
124
 
121
125
  Type definitions are included. The package also extends vite-imagetools types for better TypeScript support:
122
126
 
123
127
  ```typescript
124
- import type { ImageProps } from "@son426/vite-image/react";
128
+ import Image from "@son426/vite-image/react";
129
+ import type { ImageProps, ResponsiveImageData } from "@son426/vite-image/react";
125
130
  ```
126
131
 
132
+ ## How It Works
133
+
134
+ 1. **`?vite-image` Query**: When you import an image with `?vite-image`, the plugin automatically generates:
135
+
136
+ - Responsive srcSet (640px, 1024px, 1920px widths)
137
+ - Image metadata (1920px width)
138
+ - LQIP (20px width, blurred, low quality, inline base64)
139
+
140
+ 2. **Image Component**: The `<Image />` component handles:
141
+ - LQIP display while the main image loads
142
+ - Responsive image loading with srcSet
143
+ - Proper aspect ratio maintenance
144
+ - Fill mode for container-filling images
145
+
127
146
  ## License
128
147
 
129
148
  MIT
130
-
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { Image, ImageProps } from './react/index.js';
1
+ export { ImageProps, ResponsiveImageData } from './react/index.js';
2
2
  export { ViteImagePluginOptions, viteImage } from './plugin/index.js';
3
3
  import 'react/jsx-runtime';
4
4
  import 'react';
package/dist/index.js CHANGED
@@ -1,77 +1,39 @@
1
- import { useState } from 'react';
2
- import { jsxs, jsx } from 'react/jsx-runtime';
1
+ import 'react';
2
+ import 'react/jsx-runtime';
3
3
  import { imagetools } from 'vite-imagetools';
4
4
 
5
5
  // src/react/Image.tsx
6
- function Image({
7
- src,
8
- srcSet,
9
- lqipSrc,
10
- width,
11
- height,
12
- fill = false,
13
- sizes = "100vw",
14
- className = "",
15
- ...props
16
- }) {
17
- const [isImageLoaded, setIsImageLoaded] = useState(false);
18
- const containerStyle = fill ? {
19
- position: "absolute",
20
- top: 0,
21
- left: 0,
22
- right: 0,
23
- bottom: 0,
24
- width: "100%",
25
- height: "100%",
26
- overflow: "hidden"
27
- } : {
28
- position: "relative",
29
- width: "100%",
30
- overflow: "hidden",
31
- ...!fill && width && height ? { aspectRatio: `${width} / ${height}` } : {}
32
- };
33
- const imageStyle = {
34
- position: "absolute",
35
- top: 0,
36
- left: 0,
37
- right: 0,
38
- bottom: 0,
39
- width: "100%",
40
- height: "100%",
41
- objectFit: "cover"
42
- };
43
- const lqipStyle = {
44
- position: "absolute",
45
- top: 0,
46
- left: 0,
47
- right: 0,
48
- bottom: 0,
49
- width: "100%",
50
- height: "100%",
51
- objectFit: "cover",
52
- transition: "opacity 300ms ease-in-out",
53
- transform: "scale(1.1)",
54
- opacity: isImageLoaded ? 0 : 1
55
- };
56
- return /* @__PURE__ */ jsxs("div", { className: className || void 0, style: containerStyle, children: [
57
- /* @__PURE__ */ jsx(
58
- "img",
59
- {
60
- ...props,
61
- src,
62
- srcSet,
63
- sizes,
64
- onLoad: () => setIsImageLoaded(true),
65
- style: imageStyle
66
- }
67
- ),
68
- lqipSrc && /* @__PURE__ */ jsx("img", { src: lqipSrc, alt: "", "aria-hidden": "true", style: lqipStyle })
69
- ] });
70
- }
71
6
  function viteImage(options) {
72
- return imagetools(options);
7
+ const viteImageMacro = {
8
+ name: "vite-plugin-vite-image-macro",
9
+ enforce: "pre",
10
+ async load(id) {
11
+ const [basePath, search] = id.split("?");
12
+ const params = new URLSearchParams(search);
13
+ if (params.has("vite-image")) {
14
+ const srcSetParams = "w=640;1024;1920&format=webp&as=srcset";
15
+ const metaParams = "w=1920&format=webp&as=meta";
16
+ const lqipParams = "w=20&blur=2&quality=20&format=webp&inline";
17
+ return `
18
+ import srcSet from "${basePath}?${srcSetParams}";
19
+ import meta from "${basePath}?${metaParams}";
20
+ import lqipSrc from "${basePath}?${lqipParams}";
21
+
22
+ export default {
23
+ src: meta.src,
24
+ width: meta.width,
25
+ height: meta.height,
26
+ srcSet: srcSet,
27
+ lqipSrc: lqipSrc
28
+ };
29
+ `;
30
+ }
31
+ return null;
32
+ }
33
+ };
34
+ return [viteImageMacro, imagetools(options)];
73
35
  }
74
36
 
75
- export { Image, viteImage };
37
+ export { viteImage };
76
38
  //# sourceMappingURL=index.js.map
77
39
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react/Image.tsx","../src/plugin/index.ts"],"names":[],"mappings":";;;;;AAuBO,SAAS,KAAA,CAAM;AAAA,EACpB,GAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACA,IAAA,GAAO,KAAA;AAAA,EACP,KAAA,GAAQ,OAAA;AAAA,EACR,SAAA,GAAY,EAAA;AAAA,EACZ,GAAG;AACL,CAAA,EAAe;AACb,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,SAAS,KAAK,CAAA;AAGxD,EAAA,MAAM,iBAAgC,IAAA,GAClC;AAAA,IACE,QAAA,EAAU,UAAA;AAAA,IACV,GAAA,EAAK,CAAA;AAAA,IACL,IAAA,EAAM,CAAA;AAAA,IACN,KAAA,EAAO,CAAA;AAAA,IACP,MAAA,EAAQ,CAAA;AAAA,IACR,KAAA,EAAO,MAAA;AAAA,IACP,MAAA,EAAQ,MAAA;AAAA,IACR,QAAA,EAAU;AAAA,GACZ,GACA;AAAA,IACE,QAAA,EAAU,UAAA;AAAA,IACV,KAAA,EAAO,MAAA;AAAA,IACP,QAAA,EAAU,QAAA;AAAA,IACV,GAAI,CAAC,IAAA,IAAQ,KAAA,IAAS,MAAA,GAClB,EAAE,WAAA,EAAa,CAAA,EAAG,KAAK,CAAA,GAAA,EAAM,MAAM,CAAA,CAAA,KACnC;AAAC,GACP;AAGJ,EAAA,MAAM,UAAA,GAA4B;AAAA,IAChC,QAAA,EAAU,UAAA;AAAA,IACV,GAAA,EAAK,CAAA;AAAA,IACL,IAAA,EAAM,CAAA;AAAA,IACN,KAAA,EAAO,CAAA;AAAA,IACP,MAAA,EAAQ,CAAA;AAAA,IACR,KAAA,EAAO,MAAA;AAAA,IACP,MAAA,EAAQ,MAAA;AAAA,IACR,SAAA,EAAW;AAAA,GACb;AAGA,EAAA,MAAM,SAAA,GAA2B;AAAA,IAC/B,QAAA,EAAU,UAAA;AAAA,IACV,GAAA,EAAK,CAAA;AAAA,IACL,IAAA,EAAM,CAAA;AAAA,IACN,KAAA,EAAO,CAAA;AAAA,IACP,MAAA,EAAQ,CAAA;AAAA,IACR,KAAA,EAAO,MAAA;AAAA,IACP,MAAA,EAAQ,MAAA;AAAA,IACR,SAAA,EAAW,OAAA;AAAA,IACX,UAAA,EAAY,2BAAA;AAAA,IACZ,SAAA,EAAW,YAAA;AAAA,IACX,OAAA,EAAS,gBAAgB,CAAA,GAAI;AAAA,GAC/B;AAEA,EAAA,4BACG,KAAA,EAAA,EAAI,SAAA,EAAW,SAAA,IAAa,MAAA,EAAW,OAAO,cAAA,EAC7C,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACE,GAAG,KAAA;AAAA,QACJ,GAAA;AAAA,QACA,MAAA;AAAA,QACA,KAAA;AAAA,QACA,MAAA,EAAQ,MAAM,gBAAA,CAAiB,IAAI,CAAA;AAAA,QACnC,KAAA,EAAO;AAAA;AAAA,KACT;AAAA,IACC,OAAA,oBACC,GAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,OAAA,EAAS,KAAI,EAAA,EAAG,aAAA,EAAY,MAAA,EAAO,KAAA,EAAO,SAAA,EAAW;AAAA,GAAA,EAEnE,CAAA;AAEJ;ACpEO,SAAS,UACd,OAAA,EACQ;AACR,EAAA,OAAO,WAAW,OAAO,CAAA;AAC3B","file":"index.js","sourcesContent":["import { useState, type ImgHTMLAttributes, type CSSProperties } from \"react\";\n\ninterface BaseImageProps\n extends Omit<ImgHTMLAttributes<HTMLImageElement>, \"width\" | \"height\"> {\n src: string;\n srcSet?: string;\n lqipSrc?: string;\n}\n\ninterface FillImageProps extends BaseImageProps {\n fill: true;\n width?: never;\n height?: never;\n}\n\ninterface StandardImageProps extends BaseImageProps {\n fill: false | undefined;\n width: number;\n height: number;\n}\n\nexport type ImageProps = FillImageProps | StandardImageProps;\n\nexport function Image({\n src,\n srcSet,\n lqipSrc,\n width,\n height,\n fill = false,\n sizes = \"100vw\",\n className = \"\",\n ...props\n}: ImageProps) {\n const [isImageLoaded, setIsImageLoaded] = useState(false);\n\n // 컨테이너 스타일\n const containerStyle: CSSProperties = fill\n ? {\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n }\n : {\n position: \"relative\",\n width: \"100%\",\n overflow: \"hidden\",\n ...(!fill && width && height\n ? { aspectRatio: `${width} / ${height}` }\n : {}),\n };\n\n // 실제 이미지 스타일\n const imageStyle: CSSProperties = {\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\",\n };\n\n // LQIP 이미지 스타일\n const lqipStyle: CSSProperties = {\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\",\n transition: \"opacity 300ms ease-in-out\",\n transform: \"scale(1.1)\",\n opacity: isImageLoaded ? 0 : 1,\n };\n\n return (\n <div className={className || undefined} style={containerStyle}>\n <img\n {...props}\n src={src}\n srcSet={srcSet}\n sizes={sizes}\n onLoad={() => setIsImageLoaded(true)}\n style={imageStyle}\n />\n {lqipSrc && (\n <img src={lqipSrc} alt=\"\" aria-hidden=\"true\" style={lqipStyle} />\n )}\n </div>\n );\n}\n","import type { Plugin } from \"vite\";\nimport { imagetools } from \"vite-imagetools\";\n\nexport type ViteImagePluginOptions = Parameters<typeof imagetools>[0];\n\n/**\n * Vite plugin for image optimization using vite-imagetools\n * \n * @param options - Options to pass to vite-imagetools\n * @returns Vite plugin\n * \n * @example\n * ```ts\n * // vite.config.ts\n * import { defineConfig } from 'vite';\n * import { viteImage } from '@son426/vite-image/plugin';\n * \n * export default defineConfig({\n * plugins: [\n * viteImage({\n * defaultDirectives: (url) => {\n * if (url.searchParams.has('webp')) {\n * return new URLSearchParams('format=webp');\n * }\n * return new URLSearchParams();\n * },\n * }),\n * ],\n * });\n * ```\n */\nexport function viteImage(\n options?: ViteImagePluginOptions\n): Plugin {\n return imagetools(options);\n}\n\n"]}
1
+ {"version":3,"sources":["../src/plugin/index.ts"],"names":[],"mappings":";;;;;AAyBO,SAAS,UAAU,OAAA,EAAkD;AAE1E,EAAA,MAAM,cAAA,GAA+B;AAAA,IACnC,IAAA,EAAM,8BAAA;AAAA,IACN,OAAA,EAAS,KAAA;AAAA,IACT,MAAM,KAAK,EAAA,EAAY;AACrB,MAAA,MAAM,CAAC,QAAA,EAAU,MAAM,CAAA,GAAI,EAAA,CAAG,MAAM,GAAG,CAAA;AACvC,MAAA,MAAM,MAAA,GAAS,IAAI,eAAA,CAAgB,MAAM,CAAA;AAEzC,MAAA,IAAI,MAAA,CAAO,GAAA,CAAI,YAAY,CAAA,EAAG;AAC5B,QAAA,MAAM,YAAA,GAAe,uCAAA;AACrB,QAAA,MAAM,UAAA,GAAa,4BAAA;AACnB,QAAA,MAAM,UAAA,GAAa,2CAAA;AAEnB,QAAA,OAAO;AAAA,8BAAA,EACiB,QAAQ,IAAI,YAAY,CAAA;AAAA,4BAAA,EAC1B,QAAQ,IAAI,UAAU,CAAA;AAAA,+BAAA,EACnB,QAAQ,IAAI,UAAU,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAAA,CAAA;AAAA,MAUjD;AAEA,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,GACF;AAEA,EAAA,OAAO,CAAC,cAAA,EAAgB,UAAA,CAAW,OAAO,CAAC,CAAA;AAC7C","file":"index.js","sourcesContent":["import type { PluginOption } from \"vite\";\nimport { imagetools } from \"vite-imagetools\";\n\nexport type ViteImagePluginOptions = Parameters<typeof imagetools>[0];\n\n/**\n * Vite plugin for image optimization using vite-imagetools\n * This plugin handles ?vite-image queries and uses imagetools for image processing\n *\n * @param options - Options to pass to vite-imagetools\n * @returns Array of Vite plugins (vite-image macro and imagetools)\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { defineConfig } from 'vite';\n * import { viteImage } from '@son426/vite-image/plugin';\n *\n * export default defineConfig({\n * plugins: [\n * ...viteImage(),\n * ],\n * });\n * ```\n */\nexport function viteImage(options?: ViteImagePluginOptions): PluginOption[] {\n // 커스텀 플러그인: ?vite-image 쿼리를 처리\n const viteImageMacro: PluginOption = {\n name: \"vite-plugin-vite-image-macro\",\n enforce: \"pre\" as const,\n async load(id: string) {\n const [basePath, search] = id.split(\"?\");\n const params = new URLSearchParams(search);\n\n if (params.has(\"vite-image\")) {\n const srcSetParams = \"w=640;1024;1920&format=webp&as=srcset\";\n const metaParams = \"w=1920&format=webp&as=meta\";\n const lqipParams = \"w=20&blur=2&quality=20&format=webp&inline\";\n\n return `\n import srcSet from \"${basePath}?${srcSetParams}\";\n import meta from \"${basePath}?${metaParams}\";\n import lqipSrc from \"${basePath}?${lqipParams}\";\n \n export default {\n src: meta.src,\n width: meta.width,\n height: meta.height,\n srcSet: srcSet,\n lqipSrc: lqipSrc\n };\n `;\n }\n\n return null;\n },\n };\n\n return [viteImageMacro, imagetools(options)];\n}\n"]}
@@ -1,12 +1,13 @@
1
- import { Plugin } from 'vite';
1
+ import { PluginOption } from 'vite';
2
2
  import { imagetools } from 'vite-imagetools';
3
3
 
4
4
  type ViteImagePluginOptions = Parameters<typeof imagetools>[0];
5
5
  /**
6
6
  * Vite plugin for image optimization using vite-imagetools
7
+ * This plugin handles ?vite-image queries and uses imagetools for image processing
7
8
  *
8
9
  * @param options - Options to pass to vite-imagetools
9
- * @returns Vite plugin
10
+ * @returns Array of Vite plugins (vite-image macro and imagetools)
10
11
  *
11
12
  * @example
12
13
  * ```ts
@@ -16,18 +17,11 @@ type ViteImagePluginOptions = Parameters<typeof imagetools>[0];
16
17
  *
17
18
  * export default defineConfig({
18
19
  * plugins: [
19
- * viteImage({
20
- * defaultDirectives: (url) => {
21
- * if (url.searchParams.has('webp')) {
22
- * return new URLSearchParams('format=webp');
23
- * }
24
- * return new URLSearchParams();
25
- * },
26
- * }),
20
+ * ...viteImage(),
27
21
  * ],
28
22
  * });
29
23
  * ```
30
24
  */
31
- declare function viteImage(options?: ViteImagePluginOptions): Plugin;
25
+ declare function viteImage(options?: ViteImagePluginOptions): PluginOption[];
32
26
 
33
27
  export { type ViteImagePluginOptions, viteImage };
@@ -2,7 +2,34 @@ import { imagetools } from 'vite-imagetools';
2
2
 
3
3
  // src/plugin/index.ts
4
4
  function viteImage(options) {
5
- return imagetools(options);
5
+ const viteImageMacro = {
6
+ name: "vite-plugin-vite-image-macro",
7
+ enforce: "pre",
8
+ async load(id) {
9
+ const [basePath, search] = id.split("?");
10
+ const params = new URLSearchParams(search);
11
+ if (params.has("vite-image")) {
12
+ const srcSetParams = "w=640;1024;1920&format=webp&as=srcset";
13
+ const metaParams = "w=1920&format=webp&as=meta";
14
+ const lqipParams = "w=20&blur=2&quality=20&format=webp&inline";
15
+ return `
16
+ import srcSet from "${basePath}?${srcSetParams}";
17
+ import meta from "${basePath}?${metaParams}";
18
+ import lqipSrc from "${basePath}?${lqipParams}";
19
+
20
+ export default {
21
+ src: meta.src,
22
+ width: meta.width,
23
+ height: meta.height,
24
+ srcSet: srcSet,
25
+ lqipSrc: lqipSrc
26
+ };
27
+ `;
28
+ }
29
+ return null;
30
+ }
31
+ };
32
+ return [viteImageMacro, imagetools(options)];
6
33
  }
7
34
 
8
35
  export { viteImage };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugin/index.ts"],"names":[],"mappings":";;;AA+BO,SAAS,UACd,OAAA,EACQ;AACR,EAAA,OAAO,WAAW,OAAO,CAAA;AAC3B","file":"index.js","sourcesContent":["import type { Plugin } from \"vite\";\nimport { imagetools } from \"vite-imagetools\";\n\nexport type ViteImagePluginOptions = Parameters<typeof imagetools>[0];\n\n/**\n * Vite plugin for image optimization using vite-imagetools\n * \n * @param options - Options to pass to vite-imagetools\n * @returns Vite plugin\n * \n * @example\n * ```ts\n * // vite.config.ts\n * import { defineConfig } from 'vite';\n * import { viteImage } from '@son426/vite-image/plugin';\n * \n * export default defineConfig({\n * plugins: [\n * viteImage({\n * defaultDirectives: (url) => {\n * if (url.searchParams.has('webp')) {\n * return new URLSearchParams('format=webp');\n * }\n * return new URLSearchParams();\n * },\n * }),\n * ],\n * });\n * ```\n */\nexport function viteImage(\n options?: ViteImagePluginOptions\n): Plugin {\n return imagetools(options);\n}\n\n"]}
1
+ {"version":3,"sources":["../../src/plugin/index.ts"],"names":[],"mappings":";;;AAyBO,SAAS,UAAU,OAAA,EAAkD;AAE1E,EAAA,MAAM,cAAA,GAA+B;AAAA,IACnC,IAAA,EAAM,8BAAA;AAAA,IACN,OAAA,EAAS,KAAA;AAAA,IACT,MAAM,KAAK,EAAA,EAAY;AACrB,MAAA,MAAM,CAAC,QAAA,EAAU,MAAM,CAAA,GAAI,EAAA,CAAG,MAAM,GAAG,CAAA;AACvC,MAAA,MAAM,MAAA,GAAS,IAAI,eAAA,CAAgB,MAAM,CAAA;AAEzC,MAAA,IAAI,MAAA,CAAO,GAAA,CAAI,YAAY,CAAA,EAAG;AAC5B,QAAA,MAAM,YAAA,GAAe,uCAAA;AACrB,QAAA,MAAM,UAAA,GAAa,4BAAA;AACnB,QAAA,MAAM,UAAA,GAAa,2CAAA;AAEnB,QAAA,OAAO;AAAA,8BAAA,EACiB,QAAQ,IAAI,YAAY,CAAA;AAAA,4BAAA,EAC1B,QAAQ,IAAI,UAAU,CAAA;AAAA,+BAAA,EACnB,QAAQ,IAAI,UAAU,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAAA,CAAA;AAAA,MAUjD;AAEA,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,GACF;AAEA,EAAA,OAAO,CAAC,cAAA,EAAgB,UAAA,CAAW,OAAO,CAAC,CAAA;AAC7C","file":"index.js","sourcesContent":["import type { PluginOption } from \"vite\";\nimport { imagetools } from \"vite-imagetools\";\n\nexport type ViteImagePluginOptions = Parameters<typeof imagetools>[0];\n\n/**\n * Vite plugin for image optimization using vite-imagetools\n * This plugin handles ?vite-image queries and uses imagetools for image processing\n *\n * @param options - Options to pass to vite-imagetools\n * @returns Array of Vite plugins (vite-image macro and imagetools)\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { defineConfig } from 'vite';\n * import { viteImage } from '@son426/vite-image/plugin';\n *\n * export default defineConfig({\n * plugins: [\n * ...viteImage(),\n * ],\n * });\n * ```\n */\nexport function viteImage(options?: ViteImagePluginOptions): PluginOption[] {\n // 커스텀 플러그인: ?vite-image 쿼리를 처리\n const viteImageMacro: PluginOption = {\n name: \"vite-plugin-vite-image-macro\",\n enforce: \"pre\" as const,\n async load(id: string) {\n const [basePath, search] = id.split(\"?\");\n const params = new URLSearchParams(search);\n\n if (params.has(\"vite-image\")) {\n const srcSetParams = \"w=640;1024;1920&format=webp&as=srcset\";\n const metaParams = \"w=1920&format=webp&as=meta\";\n const lqipParams = \"w=20&blur=2&quality=20&format=webp&inline\";\n\n return `\n import srcSet from \"${basePath}?${srcSetParams}\";\n import meta from \"${basePath}?${metaParams}\";\n import lqipSrc from \"${basePath}?${lqipParams}\";\n \n export default {\n src: meta.src,\n width: meta.width,\n height: meta.height,\n srcSet: srcSet,\n lqipSrc: lqipSrc\n };\n `;\n }\n\n return null;\n },\n };\n\n return [viteImageMacro, imagetools(options)];\n}\n"]}
@@ -1,22 +1,29 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ImgHTMLAttributes } from 'react';
3
3
 
4
- interface BaseImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "width" | "height"> {
4
+ /**
5
+ * Type definitions for vite-image
6
+ */
7
+ interface ResponsiveImageData {
5
8
  src: string;
9
+ width: number;
10
+ height: number;
6
11
  srcSet?: string;
7
12
  lqipSrc?: string;
8
13
  }
14
+
15
+ interface BaseImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "src" | "srcSet" | "width" | "height"> {
16
+ src: ResponsiveImageData;
17
+ sizes?: string;
18
+ }
9
19
  interface FillImageProps extends BaseImageProps {
10
20
  fill: true;
11
- width?: never;
12
- height?: never;
13
21
  }
14
22
  interface StandardImageProps extends BaseImageProps {
15
- fill: false | undefined;
16
- width: number;
17
- height: number;
23
+ fill?: false | undefined;
18
24
  }
19
25
  type ImageProps = FillImageProps | StandardImageProps;
20
- declare function Image({ src, srcSet, lqipSrc, width, height, fill, sizes, className, ...props }: ImageProps): react_jsx_runtime.JSX.Element;
26
+ declare function Image({ src, // 이제 src는 객체입니다.
27
+ fill, sizes, className, style, ...props }: ImageProps): react_jsx_runtime.JSX.Element;
21
28
 
22
- export { Image, type ImageProps };
29
+ export { type ImageProps, type ResponsiveImageData, Image as default };
@@ -4,16 +4,21 @@ import { jsxs, jsx } from 'react/jsx-runtime';
4
4
  // src/react/Image.tsx
5
5
  function Image({
6
6
  src,
7
- srcSet,
8
- lqipSrc,
9
- width,
10
- height,
7
+ // 이제 이 src는 객체입니다.
11
8
  fill = false,
12
9
  sizes = "100vw",
13
10
  className = "",
11
+ style,
14
12
  ...props
15
13
  }) {
16
14
  const [isImageLoaded, setIsImageLoaded] = useState(false);
15
+ const {
16
+ src: currentSrc,
17
+ srcSet: currentSrcSet,
18
+ lqipSrc: currentLqip,
19
+ width: currentWidth,
20
+ height: currentHeight
21
+ } = src;
17
22
  const containerStyle = fill ? {
18
23
  position: "absolute",
19
24
  top: 0,
@@ -27,9 +32,11 @@ function Image({
27
32
  position: "relative",
28
33
  width: "100%",
29
34
  overflow: "hidden",
30
- ...!fill && width && height ? { aspectRatio: `${width} / ${height}` } : {}
35
+ // Standard 모드일 aspect-ratio 처리
36
+ ...currentWidth && currentHeight ? { aspectRatio: `${currentWidth} / ${currentHeight}` } : {}
31
37
  };
32
- const imageStyle = {
38
+ const mergedContainerStyle = { ...containerStyle, ...style };
39
+ const imgStyle = {
33
40
  position: "absolute",
34
41
  top: 0,
35
42
  left: 0,
@@ -40,34 +47,31 @@ function Image({
40
47
  objectFit: "cover"
41
48
  };
42
49
  const lqipStyle = {
43
- position: "absolute",
44
- top: 0,
45
- left: 0,
46
- right: 0,
47
- bottom: 0,
48
- width: "100%",
49
- height: "100%",
50
- objectFit: "cover",
51
- transition: "opacity 300ms ease-in-out",
50
+ ...imgStyle,
51
+ filter: "blur(20px)",
52
52
  transform: "scale(1.1)",
53
- opacity: isImageLoaded ? 0 : 1
53
+ transition: "opacity 500ms ease-out",
54
+ opacity: isImageLoaded ? 0 : 1,
55
+ zIndex: 1
54
56
  };
55
- return /* @__PURE__ */ jsxs("div", { className: className || void 0, style: containerStyle, children: [
57
+ return /* @__PURE__ */ jsxs("div", { className, style: mergedContainerStyle, children: [
56
58
  /* @__PURE__ */ jsx(
57
59
  "img",
58
60
  {
59
61
  ...props,
60
- src,
61
- srcSet,
62
+ src: currentSrc,
63
+ srcSet: currentSrcSet,
62
64
  sizes,
65
+ width: fill ? void 0 : currentWidth,
66
+ height: fill ? void 0 : currentHeight,
63
67
  onLoad: () => setIsImageLoaded(true),
64
- style: imageStyle
68
+ style: { ...imgStyle, zIndex: 0 }
65
69
  }
66
70
  ),
67
- lqipSrc && /* @__PURE__ */ jsx("img", { src: lqipSrc, alt: "", "aria-hidden": "true", style: lqipStyle })
71
+ currentLqip && /* @__PURE__ */ jsx("img", { src: currentLqip, alt: "", "aria-hidden": "true", style: lqipStyle })
68
72
  ] });
69
73
  }
70
74
 
71
- export { Image };
75
+ export { Image as default };
72
76
  //# sourceMappingURL=index.js.map
73
77
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/Image.tsx"],"names":[],"mappings":";;;;AAuBO,SAAS,KAAA,CAAM;AAAA,EACpB,GAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACA,IAAA,GAAO,KAAA;AAAA,EACP,KAAA,GAAQ,OAAA;AAAA,EACR,SAAA,GAAY,EAAA;AAAA,EACZ,GAAG;AACL,CAAA,EAAe;AACb,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,SAAS,KAAK,CAAA;AAGxD,EAAA,MAAM,iBAAgC,IAAA,GAClC;AAAA,IACE,QAAA,EAAU,UAAA;AAAA,IACV,GAAA,EAAK,CAAA;AAAA,IACL,IAAA,EAAM,CAAA;AAAA,IACN,KAAA,EAAO,CAAA;AAAA,IACP,MAAA,EAAQ,CAAA;AAAA,IACR,KAAA,EAAO,MAAA;AAAA,IACP,MAAA,EAAQ,MAAA;AAAA,IACR,QAAA,EAAU;AAAA,GACZ,GACA;AAAA,IACE,QAAA,EAAU,UAAA;AAAA,IACV,KAAA,EAAO,MAAA;AAAA,IACP,QAAA,EAAU,QAAA;AAAA,IACV,GAAI,CAAC,IAAA,IAAQ,KAAA,IAAS,MAAA,GAClB,EAAE,WAAA,EAAa,CAAA,EAAG,KAAK,CAAA,GAAA,EAAM,MAAM,CAAA,CAAA,KACnC;AAAC,GACP;AAGJ,EAAA,MAAM,UAAA,GAA4B;AAAA,IAChC,QAAA,EAAU,UAAA;AAAA,IACV,GAAA,EAAK,CAAA;AAAA,IACL,IAAA,EAAM,CAAA;AAAA,IACN,KAAA,EAAO,CAAA;AAAA,IACP,MAAA,EAAQ,CAAA;AAAA,IACR,KAAA,EAAO,MAAA;AAAA,IACP,MAAA,EAAQ,MAAA;AAAA,IACR,SAAA,EAAW;AAAA,GACb;AAGA,EAAA,MAAM,SAAA,GAA2B;AAAA,IAC/B,QAAA,EAAU,UAAA;AAAA,IACV,GAAA,EAAK,CAAA;AAAA,IACL,IAAA,EAAM,CAAA;AAAA,IACN,KAAA,EAAO,CAAA;AAAA,IACP,MAAA,EAAQ,CAAA;AAAA,IACR,KAAA,EAAO,MAAA;AAAA,IACP,MAAA,EAAQ,MAAA;AAAA,IACR,SAAA,EAAW,OAAA;AAAA,IACX,UAAA,EAAY,2BAAA;AAAA,IACZ,SAAA,EAAW,YAAA;AAAA,IACX,OAAA,EAAS,gBAAgB,CAAA,GAAI;AAAA,GAC/B;AAEA,EAAA,4BACG,KAAA,EAAA,EAAI,SAAA,EAAW,SAAA,IAAa,MAAA,EAAW,OAAO,cAAA,EAC7C,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACE,GAAG,KAAA;AAAA,QACJ,GAAA;AAAA,QACA,MAAA;AAAA,QACA,KAAA;AAAA,QACA,MAAA,EAAQ,MAAM,gBAAA,CAAiB,IAAI,CAAA;AAAA,QACnC,KAAA,EAAO;AAAA;AAAA,KACT;AAAA,IACC,OAAA,oBACC,GAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,OAAA,EAAS,KAAI,EAAA,EAAG,aAAA,EAAY,MAAA,EAAO,KAAA,EAAO,SAAA,EAAW;AAAA,GAAA,EAEnE,CAAA;AAEJ","file":"index.js","sourcesContent":["import { useState, type ImgHTMLAttributes, type CSSProperties } from \"react\";\n\ninterface BaseImageProps\n extends Omit<ImgHTMLAttributes<HTMLImageElement>, \"width\" | \"height\"> {\n src: string;\n srcSet?: string;\n lqipSrc?: string;\n}\n\ninterface FillImageProps extends BaseImageProps {\n fill: true;\n width?: never;\n height?: never;\n}\n\ninterface StandardImageProps extends BaseImageProps {\n fill: false | undefined;\n width: number;\n height: number;\n}\n\nexport type ImageProps = FillImageProps | StandardImageProps;\n\nexport function Image({\n src,\n srcSet,\n lqipSrc,\n width,\n height,\n fill = false,\n sizes = \"100vw\",\n className = \"\",\n ...props\n}: ImageProps) {\n const [isImageLoaded, setIsImageLoaded] = useState(false);\n\n // 컨테이너 스타일\n const containerStyle: CSSProperties = fill\n ? {\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n }\n : {\n position: \"relative\",\n width: \"100%\",\n overflow: \"hidden\",\n ...(!fill && width && height\n ? { aspectRatio: `${width} / ${height}` }\n : {}),\n };\n\n // 실제 이미지 스타일\n const imageStyle: CSSProperties = {\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\",\n };\n\n // LQIP 이미지 스타일\n const lqipStyle: CSSProperties = {\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\",\n transition: \"opacity 300ms ease-in-out\",\n transform: \"scale(1.1)\",\n opacity: isImageLoaded ? 0 : 1,\n };\n\n return (\n <div className={className || undefined} style={containerStyle}>\n <img\n {...props}\n src={src}\n srcSet={srcSet}\n sizes={sizes}\n onLoad={() => setIsImageLoaded(true)}\n style={imageStyle}\n />\n {lqipSrc && (\n <img src={lqipSrc} alt=\"\" aria-hidden=\"true\" style={lqipStyle} />\n )}\n </div>\n );\n}\n"]}
1
+ {"version":3,"sources":["../../src/react/Image.tsx"],"names":[],"mappings":";;;;AAwBe,SAAR,KAAA,CAAuB;AAAA,EAC5B,GAAA;AAAA;AAAA,EACA,IAAA,GAAO,KAAA;AAAA,EACP,KAAA,GAAQ,OAAA;AAAA,EACR,SAAA,GAAY,EAAA;AAAA,EACZ,KAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAe;AACb,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,SAAS,KAAK,CAAA;AAGxD,EAAA,MAAM;AAAA,IACJ,GAAA,EAAK,UAAA;AAAA,IACL,MAAA,EAAQ,aAAA;AAAA,IACR,OAAA,EAAS,WAAA;AAAA,IACT,KAAA,EAAO,YAAA;AAAA,IACP,MAAA,EAAQ;AAAA,GACV,GAAI,GAAA;AAGJ,EAAA,MAAM,iBAAgC,IAAA,GAClC;AAAA,IACE,QAAA,EAAU,UAAA;AAAA,IACV,GAAA,EAAK,CAAA;AAAA,IACL,IAAA,EAAM,CAAA;AAAA,IACN,KAAA,EAAO,CAAA;AAAA,IACP,MAAA,EAAQ,CAAA;AAAA,IACR,KAAA,EAAO,MAAA;AAAA,IACP,MAAA,EAAQ,MAAA;AAAA,IACR,QAAA,EAAU;AAAA,GACZ,GACA;AAAA,IACE,QAAA,EAAU,UAAA;AAAA,IACV,KAAA,EAAO,MAAA;AAAA,IACP,QAAA,EAAU,QAAA;AAAA;AAAA,IAEV,GAAI,YAAA,IAAgB,aAAA,GAChB,EAAE,WAAA,EAAa,CAAA,EAAG,YAAY,CAAA,GAAA,EAAM,aAAa,CAAA,CAAA,EAAG,GACpD;AAAC,GACP;AAEJ,EAAA,MAAM,oBAAA,GAAuB,EAAE,GAAG,cAAA,EAAgB,GAAG,KAAA,EAAM;AAG3D,EAAA,MAAM,QAAA,GAA0B;AAAA,IAC9B,QAAA,EAAU,UAAA;AAAA,IACV,GAAA,EAAK,CAAA;AAAA,IACL,IAAA,EAAM,CAAA;AAAA,IACN,KAAA,EAAO,CAAA;AAAA,IACP,MAAA,EAAQ,CAAA;AAAA,IACR,KAAA,EAAO,MAAA;AAAA,IACP,MAAA,EAAQ,MAAA;AAAA,IACR,SAAA,EAAW;AAAA,GACb;AAGA,EAAA,MAAM,SAAA,GAA2B;AAAA,IAC/B,GAAG,QAAA;AAAA,IACH,MAAA,EAAQ,YAAA;AAAA,IACR,SAAA,EAAW,YAAA;AAAA,IACX,UAAA,EAAY,wBAAA;AAAA,IACZ,OAAA,EAAS,gBAAgB,CAAA,GAAI,CAAA;AAAA,IAC7B,MAAA,EAAQ;AAAA,GACV;AAEA,EAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAsB,KAAA,EAAO,oBAAA,EAEhC,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACE,GAAG,KAAA;AAAA,QACJ,GAAA,EAAK,UAAA;AAAA,QACL,MAAA,EAAQ,aAAA;AAAA,QACR,KAAA;AAAA,QACA,KAAA,EAAO,OAAO,MAAA,GAAY,YAAA;AAAA,QAC1B,MAAA,EAAQ,OAAO,MAAA,GAAY,aAAA;AAAA,QAC3B,MAAA,EAAQ,MAAM,gBAAA,CAAiB,IAAI,CAAA;AAAA,QACnC,KAAA,EAAO,EAAE,GAAG,QAAA,EAAU,QAAQ,CAAA;AAAE;AAAA,KAClC;AAAA,IAGC,WAAA,oBACC,GAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,WAAA,EAAa,KAAI,EAAA,EAAG,aAAA,EAAY,MAAA,EAAO,KAAA,EAAO,SAAA,EAAW;AAAA,GAAA,EAEvE,CAAA;AAEJ","file":"index.js","sourcesContent":["import { useState, type ImgHTMLAttributes, type CSSProperties } from \"react\";\nimport type { ResponsiveImageData } from \"../types\";\n\n// ?vite-image import 결과 타입\ninterface BaseImageProps\n extends Omit<\n ImgHTMLAttributes<HTMLImageElement>,\n \"src\" | \"srcSet\" | \"width\" | \"height\"\n > {\n // 핵심 변경: src 무조건 최적화된 이미지 객체만 받음\n src: ResponsiveImageData;\n sizes?: string;\n}\n\ninterface FillImageProps extends BaseImageProps {\n fill: true;\n}\n\ninterface StandardImageProps extends BaseImageProps {\n fill?: false | undefined;\n}\n\nexport type ImageProps = FillImageProps | StandardImageProps;\n\nexport default function Image({\n src, // 이제 이 src는 객체입니다.\n fill = false,\n sizes = \"100vw\",\n className = \"\",\n style,\n ...props\n}: ImageProps) {\n const [isImageLoaded, setIsImageLoaded] = useState(false);\n\n // 1. 데이터 추출: src 객체에서 바로 꺼내씀 (병합 로직 제거)\n const {\n src: currentSrc,\n srcSet: currentSrcSet,\n lqipSrc: currentLqip,\n width: currentWidth,\n height: currentHeight,\n } = src;\n\n // 2. 컨테이너 스타일 계산 (기존 로직 유지)\n const containerStyle: CSSProperties = fill\n ? {\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n }\n : {\n position: \"relative\",\n width: \"100%\",\n overflow: \"hidden\",\n // Standard 모드일 때 aspect-ratio 처리\n ...(currentWidth && currentHeight\n ? { aspectRatio: `${currentWidth} / ${currentHeight}` }\n : {}),\n };\n\n const mergedContainerStyle = { ...containerStyle, ...style };\n\n // 3. 실제 이미지 스타일 (기존 로직 유지)\n const imgStyle: CSSProperties = {\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\",\n };\n\n // 4. LQIP 스타일 (기존 로직 유지)\n const lqipStyle: CSSProperties = {\n ...imgStyle,\n filter: \"blur(20px)\",\n transform: \"scale(1.1)\",\n transition: \"opacity 500ms ease-out\",\n opacity: isImageLoaded ? 0 : 1,\n zIndex: 1,\n };\n\n return (\n <div className={className} style={mergedContainerStyle}>\n {/* 실제 이미지 */}\n <img\n {...props}\n src={currentSrc}\n srcSet={currentSrcSet}\n sizes={sizes}\n width={fill ? undefined : currentWidth}\n height={fill ? undefined : currentHeight}\n onLoad={() => setIsImageLoaded(true)}\n style={{ ...imgStyle, zIndex: 0 }}\n />\n\n {/* LQIP 레이어 */}\n {currentLqip && (\n <img src={currentLqip} alt=\"\" aria-hidden=\"true\" style={lqipStyle} />\n )}\n </div>\n );\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@son426/vite-image",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "A Vite plugin and React component for optimized images with LQIP support using vite-imagetools",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,6 +22,7 @@
22
22
  },
23
23
  "files": [
24
24
  "dist",
25
+ "types.d.ts",
25
26
  "README.md",
26
27
  "LICENSE"
27
28
  ],
package/types.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ /**
4
+ * Type definitions for vite-imagetools query parameters
5
+ * These types extend the default vite-imagetools types
6
+ */
7
+
8
+ import type { ResponsiveImageData } from "./types";
9
+
10
+ // vite-image 쿼리 파라미터 타입
11
+ declare module "*?vite-image" {
12
+ const imageData: ResponsiveImageData;
13
+ export default imageData;
14
+ }
15
+
16
+ // srcset 타입
17
+ declare module "*?w=*&format=*&as=srcset" {
18
+ const srcset: string;
19
+ export default srcset;
20
+ }
21
+
22
+ // meta 타입
23
+ declare module "*?w=*&format=*&as=meta" {
24
+ interface ImageMeta {
25
+ src: string;
26
+ width: number;
27
+ height: number;
28
+ format?: string;
29
+ }
30
+ const meta: ImageMeta;
31
+ export default meta;
32
+ }
33
+
34
+ // inline (LQIP) 타입
35
+ declare module "*?w=*&blur=*&quality=*&format=*&inline" {
36
+ const src: string;
37
+ export default src;
38
+ }