@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 +89 -39
- package/dist/index.d.ts +2 -2
- package/dist/index.js +32 -70
- package/dist/index.js.map +1 -1
- package/dist/plugin/index.d.ts +8 -14
- package/dist/plugin/index.js +30 -3
- package/dist/plugin/index.js.map +1 -1
- package/dist/react/index.d.ts +20 -9
- package/dist/react/index.js +34 -20
- package/dist/react/index.js.map +1 -1
- package/package.json +2 -1
- package/types.d.ts +56 -0
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# @son426/vite-image
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
- ๐ **
|
|
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
|
-
|
|
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 {
|
|
41
|
+
import { viteImage } from "@son426/vite-image/plugin";
|
|
40
42
|
|
|
41
43
|
export default defineConfig({
|
|
42
44
|
plugins: [
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
###
|
|
121
|
+
### Image Props
|
|
102
122
|
|
|
103
|
-
| Prop
|
|
104
|
-
|
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
112
|
-
| `
|
|
113
|
-
|
|
|
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
|
-
\*
|
|
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
|
|
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 {
|
|
2
|
-
export {
|
|
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
|
|
2
|
-
import
|
|
1
|
+
import 'react';
|
|
2
|
+
import 'react/jsx-runtime';
|
|
3
3
|
import { imagetools } from 'vite-imagetools';
|
|
4
4
|
|
|
5
|
-
// src/react/
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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 {
|
|
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,33 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PluginOption } from 'vite';
|
|
2
2
|
import { imagetools } from 'vite-imagetools';
|
|
3
3
|
|
|
4
|
-
type
|
|
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
|
|
13
14
|
* // vite.config.ts
|
|
14
15
|
* import { defineConfig } from 'vite';
|
|
15
|
-
* import {
|
|
16
|
+
* import { viteImage } from '@son426/vite-image/plugin';
|
|
16
17
|
*
|
|
17
18
|
* export default defineConfig({
|
|
18
19
|
* plugins: [
|
|
19
|
-
*
|
|
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
|
|
25
|
+
declare function viteImage(options?: ViteImagePluginOptions): PluginOption[];
|
|
32
26
|
|
|
33
|
-
export { type
|
|
27
|
+
export { type ViteImagePluginOptions, viteImage };
|
package/dist/plugin/index.js
CHANGED
|
@@ -1,10 +1,37 @@
|
|
|
1
1
|
import { imagetools } from 'vite-imagetools';
|
|
2
2
|
|
|
3
3
|
// src/plugin/index.ts
|
|
4
|
-
function
|
|
5
|
-
|
|
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 {
|
|
35
|
+
export { viteImage };
|
|
9
36
|
//# sourceMappingURL=index.js.map
|
|
10
37
|
//# sourceMappingURL=index.js.map
|
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,33 @@
|
|
|
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;
|
|
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
|
|
16
|
-
width: number;
|
|
17
|
-
height: number;
|
|
28
|
+
fill?: false | undefined;
|
|
18
29
|
}
|
|
19
|
-
type
|
|
20
|
-
declare function
|
|
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 {
|
|
33
|
+
export { type ImageProps, type ResponsiveImageData, Image as default };
|
package/dist/react/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
|
-
// src/react/
|
|
5
|
-
function
|
|
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
|
-
|
|
41
|
+
// Standard ๋ชจ๋์ผ ๋ aspect-ratio ์ฒ๋ฆฌ
|
|
42
|
+
...currentWidth && currentHeight ? { aspectRatio: `${currentWidth} / ${currentHeight}` } : {}
|
|
31
43
|
};
|
|
32
|
-
const
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
78
|
+
style: { ...imgStyle, zIndex: 0 }
|
|
65
79
|
}
|
|
66
80
|
),
|
|
67
|
-
|
|
81
|
+
currentLqip && /* @__PURE__ */ jsx("img", { src: currentLqip, alt: "", "aria-hidden": "true", style: lqipStyle })
|
|
68
82
|
] });
|
|
69
83
|
}
|
|
70
84
|
|
|
71
|
-
export {
|
|
85
|
+
export { Image as default };
|
|
72
86
|
//# sourceMappingURL=index.js.map
|
|
73
87
|
//# sourceMappingURL=index.js.map
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/
|
|
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.
|
|
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
|
+
}
|