@son426/vite-image 0.1.0 โ†’ 0.1.2

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,6 +1,8 @@
1
1
  # @son426/vite-image
2
2
 
3
- A Vite plugin and React component for optimized images with LQIP (Low Quality Image Placeholder) support using `vite-imagetools`.
3
+ **Next.js-style Image component for Vite.**
4
+
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.
4
6
 
5
7
  ## Features
6
8
 
@@ -8,7 +10,7 @@ A Vite plugin and React component for optimized images with LQIP (Low Quality Im
8
10
  - ๐ŸŽจ **LQIP Support**: Low Quality Image Placeholder for smooth loading experience
9
11
  - ๐Ÿ“ฑ **Responsive Images**: Built-in support for srcSet and sizes attributes
10
12
  - ๐ŸŽฏ **TypeScript**: Full TypeScript support with type definitions
11
- - ๐Ÿš€ **Zero Config**: Works out of the box with sensible defaults
13
+ - ๐Ÿš€ **Simple Setup**: Easy configuration with sensible defaults
12
14
 
13
15
  ## Installation
14
16
 
@@ -22,7 +24,7 @@ yarn add @son426/vite-image
22
24
 
23
25
  ## Peer Dependencies
24
26
 
25
- Make sure you have these installed:
27
+ The following packages are required:
26
28
 
27
29
  ```bash
28
30
  pnpm add react vite vite-imagetools
@@ -36,36 +38,61 @@ Add the plugin to your `vite.config.ts`:
36
38
 
37
39
  ```typescript
38
40
  import { defineConfig } from "vite";
39
- import { customImage } from "@son426/vite-image/plugin";
41
+ import { viteImage } from "@son426/vite-image/plugin";
40
42
 
41
43
  export default defineConfig({
42
44
  plugins: [
43
- customImage({
44
- // Optional: vite-imagetools options
45
- defaultDirectives: (url) => {
46
- if (url.searchParams.has("webp")) {
47
- return new URLSearchParams("format=webp");
48
- }
49
- return new URLSearchParams();
50
- },
51
- }),
45
+ // ... other plugins
46
+ viteImage(),
52
47
  ],
53
48
  });
54
49
  ```
55
50
 
51
+ **Important**: Use the spread operator (`...viteImage()`) to properly register both plugins.
52
+
56
53
  ### 2. Use the Component
57
54
 
55
+ #### Method 1: Using `?vite-image` query (Recommended)
56
+
57
+ The simplest way is to use the `?vite-image` query parameter, which automatically generates all required image data:
58
+
58
59
  ```typescript
59
- import { CustomImage } from "@son426/vite-image/react";
60
+ import Image from "@son426/vite-image/react";
61
+ import bgImage from "@/assets/image.webp?vite-image";
62
+
63
+ function MyComponent() {
64
+ return (
65
+ <Image
66
+ imgData={bgImage}
67
+ fill={false}
68
+ sizes="(max-width: 640px) 100vw, (max-width: 1024px) 100vw, 1920px"
69
+ alt="Description"
70
+ />
71
+ );
72
+ }
73
+ ```
74
+
75
+ The `?vite-image` query automatically generates:
76
+
77
+ - `src`: Optimized image URL
78
+ - `srcSet`: Responsive srcSet string
79
+ - `lqipSrc`: Low Quality Image Placeholder (base64 inline)
80
+ - `width` and `height`: Image dimensions
81
+
82
+ #### Method 2: Using individual props
83
+
84
+ You can also import and use individual image data:
85
+
86
+ ```typescript
87
+ import Image from "@son426/vite-image/react";
60
88
 
61
- // Import optimized images
62
89
  import imageSrcSet from "@/assets/image.webp?w=640;1024;1920&format=webp&as=srcset";
63
90
  import imageMeta from "@/assets/image.webp?w=1920&format=webp&as=meta";
64
91
  import imageLqipSrc from "@/assets/image.webp?w=20&blur=2&quality=20&format=webp&inline";
65
92
 
66
93
  function MyComponent() {
67
94
  return (
68
- <CustomImage
95
+ <Image
69
96
  src={imageMeta.src}
70
97
  srcSet={imageSrcSet}
71
98
  lqipSrc={imageLqipSrc}
@@ -85,44 +112,67 @@ For images that fill their container (similar to Next.js Image):
85
112
 
86
113
  ```typescript
87
114
  <div style={{ position: "relative", width: "100%", height: "400px" }}>
88
- <CustomImage
89
- src={imageMeta.src}
90
- srcSet={imageSrcSet}
91
- lqipSrc={imageLqipSrc}
92
- fill={true}
93
- sizes="100vw"
94
- alt="Description"
95
- />
115
+ <Image imgData={bgImage} fill={true} sizes="100vw" alt="Description" />
96
116
  </div>
97
117
  ```
98
118
 
99
119
  ## API
100
120
 
101
- ### CustomImage Props
121
+ ### Image Props
102
122
 
103
- | Prop | Type | Required | Description |
104
- | --------- | ----------------------- | -------- | ---------------------------------------------- |
105
- | `src` | `string` | Yes | Image source URL |
106
- | `srcSet` | `string` | No | Responsive image srcSet |
107
- | `lqipSrc` | `string` | No | Low Quality Image Placeholder source |
108
- | `fill` | `boolean` | No | Fill container mode (default: `false`) |
109
- | `width` | `number` | Yes* | Image width (required when `fill={false}`) |
110
- | `height` | `number` | Yes* | Image height (required when `fill={false}`) |
111
- | `sizes` | `string` | No | Sizes attribute (default: `"100vw"`) |
112
- | `className` | `string` | No | Additional CSS classes |
113
- | `...props`| `ImgHTMLAttributes` | No | All standard img element attributes |
123
+ | Prop | Type | Required | Description |
124
+ | ----------- | --------------------- | -------- | ------------------------------------------- |
125
+ | `imgData` | `ResponsiveImageData` | No\* | Image data object from `?vite-image` query |
126
+ | `src` | `string` | Yes\* | Image source URL |
127
+ | `srcSet` | `string` | No | Responsive image srcSet |
128
+ | `lqipSrc` | `string` | No | Low Quality Image Placeholder source |
129
+ | `fill` | `boolean` | No | Fill container mode (default: `false`) |
130
+ | `width` | `number` | Yes\* | Image width (required when `fill={false}`) |
131
+ | `height` | `number` | Yes\* | Image height (required when `fill={false}`) |
132
+ | `sizes` | `string` | No | Sizes attribute (default: `"100vw"`) |
133
+ | `className` | `string` | No | Additional CSS classes |
134
+ | `style` | `CSSProperties` | No | Additional inline styles |
135
+ | `...props` | `ImgHTMLAttributes` | No | All standard img element attributes |
114
136
 
115
- \* Required when `fill={false}`
137
+ \* Either `imgData` or `src` (with `width` and `height` when `fill={false}`) is required.
138
+
139
+ ### ResponsiveImageData
140
+
141
+ The type returned from `?vite-image` query:
142
+
143
+ ```typescript
144
+ interface ResponsiveImageData {
145
+ src: string;
146
+ width: number;
147
+ height: number;
148
+ srcSet?: string;
149
+ lqipSrc?: string;
150
+ }
151
+ ```
116
152
 
117
153
  ## TypeScript
118
154
 
119
155
  Type definitions are included. The package also extends vite-imagetools types for better TypeScript support:
120
156
 
121
157
  ```typescript
122
- import type { CustomImageProps } from "@son426/vite-image/react";
158
+ import Image from "@son426/vite-image/react";
159
+ import type { ImageProps, ResponsiveImageData } from "@son426/vite-image/react";
123
160
  ```
124
161
 
162
+ ## How It Works
163
+
164
+ 1. **`?vite-image` Query**: When you import an image with `?vite-image`, the plugin automatically generates:
165
+
166
+ - Responsive srcSet (640px, 1024px, 1920px widths)
167
+ - Image metadata (1920px width)
168
+ - LQIP (20px width, blurred, low quality, inline base64)
169
+
170
+ 2. **Image Component**: The `<Image />` component handles:
171
+ - LQIP display while the main image loads
172
+ - Responsive image loading with srcSet
173
+ - Proper aspect ratio maintenance
174
+ - Fill mode for container-filling images
175
+
125
176
  ## License
126
177
 
127
178
  MIT
128
-
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { CustomImage, CustomImageProps } from './react/index.js';
2
- export { CustomImagePluginOptions, customImage } from './plugin/index.js';
1
+ export { ImageProps, ResponsiveImageData } from './react/index.js';
2
+ export { ViteImagePluginOptions, viteImage } from './plugin/index.js';
3
3
  import 'react/jsx-runtime';
4
4
  import 'react';
5
5
  import 'vite';
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
- // src/react/CustomImage.tsx
6
- function CustomImage({
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
5
+ // src/react/Image.tsx
6
+ function viteImage(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
+ `;
66
30
  }
67
- ),
68
- lqipSrc && /* @__PURE__ */ jsx("img", { src: lqipSrc, alt: "", "aria-hidden": "true", style: lqipStyle })
69
- ] });
70
- }
71
- function customImage(options) {
72
- return imagetools(options);
31
+ return null;
32
+ }
33
+ };
34
+ return [viteImageMacro, imagetools(options)];
73
35
  }
74
36
 
75
- export { CustomImage, customImage };
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/CustomImage.tsx","../src/plugin/index.ts"],"names":[],"mappings":";;;;;AAuBO,SAAS,WAAA,CAAY;AAAA,EAC1B,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,EAAqB;AACnB,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,YACd,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 CustomImageProps = FillImageProps | StandardImageProps;\n\nexport function CustomImage({\n src,\n srcSet,\n lqipSrc,\n width,\n height,\n fill = false,\n sizes = \"100vw\",\n className = \"\",\n ...props\n}: CustomImageProps) {\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\n","import type { Plugin } from \"vite\";\nimport { imagetools } from \"vite-imagetools\";\n\nexport type CustomImagePluginOptions = 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 { customImage } from '@son426/vite-image/plugin';\n * \n * export default defineConfig({\n * plugins: [\n * customImage({\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 customImage(\n options?: CustomImagePluginOptions\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,33 +1,27 @@
1
- import { Plugin } from 'vite';
1
+ import { PluginOption } from 'vite';
2
2
  import { imagetools } from 'vite-imagetools';
3
3
 
4
- type CustomImagePluginOptions = Parameters<typeof imagetools>[0];
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
13
14
  * // vite.config.ts
14
15
  * import { defineConfig } from 'vite';
15
- * import { customImage } from '@son426/vite-image/plugin';
16
+ * import { viteImage } from '@son426/vite-image/plugin';
16
17
  *
17
18
  * export default defineConfig({
18
19
  * plugins: [
19
- * customImage({
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 customImage(options?: CustomImagePluginOptions): Plugin;
25
+ declare function viteImage(options?: ViteImagePluginOptions): PluginOption[];
32
26
 
33
- export { type CustomImagePluginOptions, customImage };
27
+ export { type ViteImagePluginOptions, viteImage };
@@ -1,10 +1,37 @@
1
1
  import { imagetools } from 'vite-imagetools';
2
2
 
3
3
  // src/plugin/index.ts
4
- function customImage(options) {
5
- return imagetools(options);
4
+ function viteImage(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
- export { customImage };
35
+ export { viteImage };
9
36
  //# sourceMappingURL=index.js.map
10
37
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugin/index.ts"],"names":[],"mappings":";;;AA+BO,SAAS,YACd,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 CustomImagePluginOptions = 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 { customImage } from '@son426/vite-image/plugin';\n * \n * export default defineConfig({\n * plugins: [\n * customImage({\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 customImage(\n options?: CustomImagePluginOptions\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,33 @@
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;
11
+ srcSet?: string;
12
+ lqipSrc?: string;
13
+ }
14
+
15
+ interface BaseImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "width" | "height" | "src" | "srcSet"> {
16
+ sizes?: string;
17
+ src?: string;
6
18
  srcSet?: string;
7
19
  lqipSrc?: string;
20
+ width?: number;
21
+ height?: number;
22
+ imgData?: ResponsiveImageData;
8
23
  }
9
24
  interface FillImageProps extends BaseImageProps {
10
25
  fill: true;
11
- width?: never;
12
- height?: never;
13
26
  }
14
27
  interface StandardImageProps extends BaseImageProps {
15
- fill: false | undefined;
16
- width: number;
17
- height: number;
28
+ fill?: false | undefined;
18
29
  }
19
- type CustomImageProps = FillImageProps | StandardImageProps;
20
- declare function CustomImage({ src, srcSet, lqipSrc, width, height, fill, sizes, className, ...props }: CustomImageProps): react_jsx_runtime.JSX.Element;
30
+ type ImageProps = FillImageProps | StandardImageProps;
31
+ declare function Image({ imgData, src, srcSet, lqipSrc, width, height, fill, sizes, className, style, ...props }: ImageProps): react_jsx_runtime.JSX.Element | null;
21
32
 
22
- export { CustomImage, type CustomImageProps };
33
+ export { type ImageProps, type ResponsiveImageData, Image as default };
@@ -1,8 +1,9 @@
1
1
  import { useState } from 'react';
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
 
4
- // src/react/CustomImage.tsx
5
- function CustomImage({
4
+ // src/react/Image.tsx
5
+ function Image({
6
+ imgData,
6
7
  src,
7
8
  srcSet,
8
9
  lqipSrc,
@@ -11,9 +12,19 @@ function CustomImage({
11
12
  fill = false,
12
13
  sizes = "100vw",
13
14
  className = "",
15
+ style,
14
16
  ...props
15
17
  }) {
16
18
  const [isImageLoaded, setIsImageLoaded] = useState(false);
19
+ const currentSrc = imgData?.src || src;
20
+ const currentSrcSet = imgData?.srcSet || srcSet;
21
+ const currentLqip = imgData?.lqipSrc || lqipSrc;
22
+ const currentWidth = imgData?.width || width;
23
+ const currentHeight = imgData?.height || height;
24
+ if (!currentSrc) {
25
+ console.warn("Image: 'src' or 'imgData' is required.");
26
+ return null;
27
+ }
17
28
  const containerStyle = fill ? {
18
29
  position: "absolute",
19
30
  top: 0,
@@ -27,9 +38,11 @@ function CustomImage({
27
38
  position: "relative",
28
39
  width: "100%",
29
40
  overflow: "hidden",
30
- ...!fill && width && height ? { aspectRatio: `${width} / ${height}` } : {}
41
+ // Standard ๋ชจ๋“œ์ผ ๋•Œ aspect-ratio ์ฒ˜๋ฆฌ
42
+ ...currentWidth && currentHeight ? { aspectRatio: `${currentWidth} / ${currentHeight}` } : {}
31
43
  };
32
- const imageStyle = {
44
+ const mergedContainerStyle = { ...containerStyle, ...style };
45
+ const imgStyle = {
33
46
  position: "absolute",
34
47
  top: 0,
35
48
  left: 0,
@@ -40,34 +53,35 @@ function CustomImage({
40
53
  objectFit: "cover"
41
54
  };
42
55
  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",
56
+ ...imgStyle,
57
+ filter: "blur(20px)",
58
+ // blur ํšจ๊ณผ ๊ฐ•ํ™” (์„ ํƒ์‚ฌํ•ญ)
52
59
  transform: "scale(1.1)",
53
- opacity: isImageLoaded ? 0 : 1
60
+ // blur ๊ฒฝ๊ณ„์„  ์ˆจ๊ธฐ๊ธฐ
61
+ transition: "opacity 500ms ease-out",
62
+ // ๋ถ€๋“œ๋Ÿฌ์šด ์ „ํ™˜
63
+ opacity: isImageLoaded ? 0 : 1,
64
+ zIndex: 1
65
+ // ๋กœ๋”ฉ ์ค‘์—๋Š” ์œ„์— ํ‘œ์‹œ
54
66
  };
55
- return /* @__PURE__ */ jsxs("div", { className: className || void 0, style: containerStyle, children: [
67
+ return /* @__PURE__ */ jsxs("div", { className, style: mergedContainerStyle, children: [
56
68
  /* @__PURE__ */ jsx(
57
69
  "img",
58
70
  {
59
71
  ...props,
60
- src,
61
- srcSet,
72
+ src: currentSrc,
73
+ srcSet: currentSrcSet,
62
74
  sizes,
75
+ width: fill ? void 0 : currentWidth,
76
+ height: fill ? void 0 : currentHeight,
63
77
  onLoad: () => setIsImageLoaded(true),
64
- style: imageStyle
78
+ style: { ...imgStyle, zIndex: 0 }
65
79
  }
66
80
  ),
67
- lqipSrc && /* @__PURE__ */ jsx("img", { src: lqipSrc, alt: "", "aria-hidden": "true", style: lqipStyle })
81
+ currentLqip && /* @__PURE__ */ jsx("img", { src: currentLqip, alt: "", "aria-hidden": "true", style: lqipStyle })
68
82
  ] });
69
83
  }
70
84
 
71
- export { CustomImage };
85
+ export { Image as default };
72
86
  //# sourceMappingURL=index.js.map
73
87
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/CustomImage.tsx"],"names":[],"mappings":";;;;AAuBO,SAAS,WAAA,CAAY;AAAA,EAC1B,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,EAAqB;AACnB,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 CustomImageProps = FillImageProps | StandardImageProps;\n\nexport function CustomImage({\n src,\n srcSet,\n lqipSrc,\n width,\n height,\n fill = false,\n sizes = \"100vw\",\n className = \"\",\n ...props\n}: CustomImageProps) {\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\n"]}
1
+ {"version":3,"sources":["../../src/react/Image.tsx"],"names":[],"mappings":"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","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 \"width\" | \"height\" | \"src\" | \"srcSet\"\n > {\n sizes?: string;\n src?: string;\n srcSet?: string;\n lqipSrc?: string;\n width?: number;\n height?: number;\n imgData?: ResponsiveImageData;\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 imgData,\n src,\n srcSet,\n lqipSrc,\n width,\n height,\n fill = false,\n sizes = \"100vw\",\n className = \"\",\n style,\n ...props\n}: ImageProps) {\n const [isImageLoaded, setIsImageLoaded] = useState(false);\n\n // 1. ๋ฐ์ดํ„ฐ ๋ณ‘ํ•ฉ: imgData๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ๊ฑธ ์“ฐ๊ณ , ์—†์œผ๋ฉด ๊ฐœ๋ณ„ prop ์‚ฌ์šฉ\n const currentSrc = imgData?.src || src;\n const currentSrcSet = imgData?.srcSet || srcSet;\n const currentLqip = imgData?.lqipSrc || lqipSrc;\n const currentWidth = imgData?.width || width;\n const currentHeight = imgData?.height || height;\n\n if (!currentSrc) {\n console.warn(\"Image: 'src' or 'imgData' is required.\");\n return null;\n }\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)\", // blur ํšจ๊ณผ ๊ฐ•ํ™” (์„ ํƒ์‚ฌํ•ญ)\n transform: \"scale(1.1)\", // blur ๊ฒฝ๊ณ„์„  ์ˆจ๊ธฐ๊ธฐ\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.0",
3
+ "version": "0.1.2",
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,56 @@
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
+ // ?vite-image import ๊ฒฐ๊ณผ ํƒ€์ž…
9
+ interface ResponsiveImageData {
10
+ src: string;
11
+ width: number;
12
+ height: number;
13
+ srcSet?: string;
14
+ lqipSrc?: string;
15
+ }
16
+
17
+ // vite-image ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…
18
+ declare module "*?vite-image" {
19
+ const imageData: ResponsiveImageData;
20
+ export default imageData;
21
+ }
22
+
23
+ // srcset ํƒ€์ž…
24
+ declare module "*?w=*&format=*&as=srcset" {
25
+ const srcset: string;
26
+ export default srcset;
27
+ }
28
+
29
+ // meta ํƒ€์ž…
30
+ declare module "*?w=*&format=*&as=meta" {
31
+ interface ImageMeta {
32
+ src: string;
33
+ width: number;
34
+ height: number;
35
+ format?: string;
36
+ }
37
+ const meta: ImageMeta;
38
+ export default meta;
39
+ }
40
+
41
+ // inline (LQIP) ํƒ€์ž…
42
+ declare module "*?w=*&blur=*&quality=*&format=*&inline" {
43
+ const src: string;
44
+ export default src;
45
+ }
46
+
47
+ // ์ผ๋ฐ˜ imagetools ํ™•์žฅ
48
+ declare module "*&imagetools" {
49
+ type ImageToolsOutput =
50
+ | string // ๊ธฐ๋ณธ ์ถœ๋ ฅ
51
+ | { src: string; width: number; height: number } // meta
52
+ | string; // srcset
53
+
54
+ const out: ImageToolsOutput;
55
+ export default out;
56
+ }