@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 +66 -48
- package/dist/index.d.ts +1 -1
- package/dist/index.js +31 -69
- package/dist/index.js.map +1 -1
- package/dist/plugin/index.d.ts +5 -11
- package/dist/plugin/index.js +28 -1
- package/dist/plugin/index.js.map +1 -1
- package/dist/react/index.d.ts +15 -8
- package/dist/react/index.js +26 -22
- package/dist/react/index.js.map +1 -1
- package/package.json +2 -1
- package/types.d.ts +38 -0
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @son426/vite-image
|
|
2
2
|
|
|
3
|
-
**Next.js
|
|
3
|
+
**Next.js-style Image component for Vite.**
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
- 🚀 **
|
|
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
|
-
|
|
46
|
-
|
|
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
|
-
|
|
61
|
-
import { Image } from "@son426/vite-image/react";
|
|
55
|
+
#### Using `?vite-image` query (Required)
|
|
62
56
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
import
|
|
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={
|
|
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
|
-
|
|
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
|
|
106
|
-
|
|
|
107
|
-
| `src`
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
112
|
-
| `
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
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
|
|
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
package/dist/index.js
CHANGED
|
@@ -1,77 +1,39 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
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
|
-
|
|
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 {
|
|
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/
|
|
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"]}
|
package/dist/plugin/index.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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):
|
|
25
|
+
declare function viteImage(options?: ViteImagePluginOptions): PluginOption[];
|
|
32
26
|
|
|
33
27
|
export { type ViteImagePluginOptions, viteImage };
|
package/dist/plugin/index.js
CHANGED
|
@@ -2,7 +2,34 @@ import { imagetools } from 'vite-imagetools';
|
|
|
2
2
|
|
|
3
3
|
// src/plugin/index.ts
|
|
4
4
|
function viteImage(options) {
|
|
5
|
-
|
|
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 };
|
package/dist/plugin/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/plugin/index.ts"],"names":[],"mappings":";;;
|
|
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"]}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ImgHTMLAttributes } from 'react';
|
|
3
3
|
|
|
4
|
-
|
|
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
|
|
16
|
-
width: number;
|
|
17
|
-
height: number;
|
|
23
|
+
fill?: false | undefined;
|
|
18
24
|
}
|
|
19
25
|
type ImageProps = FillImageProps | StandardImageProps;
|
|
20
|
-
declare function Image({ src,
|
|
26
|
+
declare function Image({ src, // 이제 이 src는 객체입니다.
|
|
27
|
+
fill, sizes, className, style, ...props }: ImageProps): react_jsx_runtime.JSX.Element;
|
|
21
28
|
|
|
22
|
-
export {
|
|
29
|
+
export { type ImageProps, type ResponsiveImageData, Image as default };
|
package/dist/react/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
35
|
+
// Standard 모드일 때 aspect-ratio 처리
|
|
36
|
+
...currentWidth && currentHeight ? { aspectRatio: `${currentWidth} / ${currentHeight}` } : {}
|
|
31
37
|
};
|
|
32
|
-
const
|
|
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
|
-
|
|
44
|
-
|
|
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
|
-
|
|
53
|
+
transition: "opacity 500ms ease-out",
|
|
54
|
+
opacity: isImageLoaded ? 0 : 1,
|
|
55
|
+
zIndex: 1
|
|
54
56
|
};
|
|
55
|
-
return /* @__PURE__ */ jsxs("div", { className
|
|
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:
|
|
68
|
+
style: { ...imgStyle, zIndex: 0 }
|
|
65
69
|
}
|
|
66
70
|
),
|
|
67
|
-
|
|
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
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/Image.tsx"],"names":[],"mappings":";;;;
|
|
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.
|
|
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
|
+
}
|