@lonik/oh-image 0.0.1 → 1.0.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 +27 -4
- package/dist/plugin.d.ts +58 -0
- package/dist/plugin.js +221 -0
- package/dist/react.d.ts +55 -0
- package/dist/react.js +73 -0
- package/package.json +61 -24
package/README.md
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
|
-
#
|
|
1
|
+
# react-components-starter
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A starter for creating a React component library.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Development
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- Install dependencies:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- Run the playground:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm run play
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- Run the unit tests:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm run test
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- Build the library:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm run build
|
|
29
|
+
```
|
|
30
|
+
# tsdown-template
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { FormatEnum } from "sharp";
|
|
2
|
+
import * as vite0 from "vite";
|
|
3
|
+
import * as rollup0 from "rollup";
|
|
4
|
+
|
|
5
|
+
//#region src/plugin/types.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Configuration options for the oh-image Vite plugin.
|
|
8
|
+
* Extends ImageOptions with all properties required, plus plugin-specific settings.
|
|
9
|
+
*/
|
|
10
|
+
interface PluginConfig extends Required<ImageOptions> {
|
|
11
|
+
/** Directory name where processed images will be output during build */
|
|
12
|
+
distDir: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Options for image processing and transformation.
|
|
16
|
+
* Can be passed via query parameters or plugin configuration.
|
|
17
|
+
*/
|
|
18
|
+
interface ImageOptions {
|
|
19
|
+
/** Target width for the processed image in pixels */
|
|
20
|
+
width?: number | null;
|
|
21
|
+
/** Target height for the processed image in pixels */
|
|
22
|
+
height?: number | null;
|
|
23
|
+
/** Output format for the main image (e.g., 'webp', 'avif', 'png') */
|
|
24
|
+
format?: keyof FormatEnum | null;
|
|
25
|
+
/** Blur amount (true for default blur, or a number for sigma value) */
|
|
26
|
+
blur?: number | boolean;
|
|
27
|
+
/** Whether to generate a placeholder image for lazy loading */
|
|
28
|
+
placeholder?: boolean;
|
|
29
|
+
/** Width of the placeholder image in pixels */
|
|
30
|
+
placeholderW?: number;
|
|
31
|
+
/** Height of the placeholder image in pixels */
|
|
32
|
+
placeholderH?: number;
|
|
33
|
+
/** Output format for the placeholder image */
|
|
34
|
+
placeholderF?: keyof FormatEnum;
|
|
35
|
+
/** Blur setting for the placeholder (true for default, or sigma value) */
|
|
36
|
+
placeholderB: boolean | number;
|
|
37
|
+
/** Breakpoints array - widths in pixels for responsive srcSet generation */
|
|
38
|
+
bps?: number[];
|
|
39
|
+
/** Output format for srcSet images */
|
|
40
|
+
srcSetsF: keyof FormatEnum;
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/plugin/plugin.d.ts
|
|
44
|
+
declare function ohImage(options?: Partial<PluginConfig>): {
|
|
45
|
+
name: string;
|
|
46
|
+
configResolved(this: vite0.MinimalPluginContextWithoutEnvironment, viteConfig: vite0.ResolvedConfig): void;
|
|
47
|
+
enforce: "pre";
|
|
48
|
+
configureServer(this: vite0.MinimalPluginContextWithoutEnvironment, server: vite0.ViteDevServer): void;
|
|
49
|
+
load: {
|
|
50
|
+
filter: {
|
|
51
|
+
id: RegExp;
|
|
52
|
+
};
|
|
53
|
+
handler(this: rollup0.PluginContext, id: string): Promise<string | null>;
|
|
54
|
+
};
|
|
55
|
+
writeBundle(this: rollup0.PluginContext): Promise<void>;
|
|
56
|
+
};
|
|
57
|
+
//#endregion
|
|
58
|
+
export { ohImage };
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { basename, dirname, extname, join, parse } from "node:path";
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import queryString from "query-string";
|
|
5
|
+
import sharp from "sharp";
|
|
6
|
+
import pLimit from "p-limit";
|
|
7
|
+
|
|
8
|
+
//#region src/plugin/utils.ts
|
|
9
|
+
function getRandomString(length = 32) {
|
|
10
|
+
return randomBytes(Math.ceil(length * 3 / 4)).toString("base64").slice(0, length).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
11
|
+
}
|
|
12
|
+
async function readFileSafe(path) {
|
|
13
|
+
try {
|
|
14
|
+
return await readFile(path);
|
|
15
|
+
} catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async function saveFileSafe(path, data) {
|
|
20
|
+
const dir = dirname(path);
|
|
21
|
+
try {
|
|
22
|
+
await mkdir(dir, { recursive: true });
|
|
23
|
+
await writeFile(path, data);
|
|
24
|
+
console.log(`Successfully saved to ${path}`);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
console.error("Failed to save file:", err);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function queryToOptions(processKey, uri) {
|
|
30
|
+
const [path, query] = uri.split("?");
|
|
31
|
+
if (!query || !path) return {
|
|
32
|
+
shouldProcess: false,
|
|
33
|
+
path: ""
|
|
34
|
+
};
|
|
35
|
+
const parsed = queryString.parse(query, {
|
|
36
|
+
parseBooleans: true,
|
|
37
|
+
parseNumbers: true,
|
|
38
|
+
arrayFormat: "comma"
|
|
39
|
+
});
|
|
40
|
+
if (processKey in parsed) return {
|
|
41
|
+
shouldProcess: true,
|
|
42
|
+
options: parsed,
|
|
43
|
+
path
|
|
44
|
+
};
|
|
45
|
+
else return {
|
|
46
|
+
shouldProcess: false,
|
|
47
|
+
path
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async function processImage(path, options) {
|
|
51
|
+
let processed = sharp(path);
|
|
52
|
+
if (options.width || options.height) processed = processed.resize({
|
|
53
|
+
width: options.width ?? void 0,
|
|
54
|
+
height: options.height ?? void 0
|
|
55
|
+
});
|
|
56
|
+
if (options.format) processed = processed.toFormat(options.format);
|
|
57
|
+
if (options.blur) processed = processed.blur(options.blur);
|
|
58
|
+
return await processed.toBuffer();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/plugin/plugin.ts
|
|
63
|
+
const DEFAULT_CONFIGS = {
|
|
64
|
+
distDir: "oh-images",
|
|
65
|
+
bps: [
|
|
66
|
+
16,
|
|
67
|
+
48,
|
|
68
|
+
96,
|
|
69
|
+
128,
|
|
70
|
+
384,
|
|
71
|
+
640,
|
|
72
|
+
750,
|
|
73
|
+
828,
|
|
74
|
+
1080,
|
|
75
|
+
1200,
|
|
76
|
+
1920
|
|
77
|
+
],
|
|
78
|
+
format: "webp",
|
|
79
|
+
blur: false,
|
|
80
|
+
width: null,
|
|
81
|
+
height: null,
|
|
82
|
+
placeholder: false,
|
|
83
|
+
placeholderH: 100,
|
|
84
|
+
placeholderW: 100,
|
|
85
|
+
placeholderB: true,
|
|
86
|
+
placeholderF: "webp",
|
|
87
|
+
srcSetsF: "webp"
|
|
88
|
+
};
|
|
89
|
+
const PROCESS_KEY = "oh";
|
|
90
|
+
const SUPPORTED_IMAGE_FORMATS = /\.(jpe?g|png|webp|avif|gif|tiff?|svg)(\?.*)?$/i;
|
|
91
|
+
const DEV_DIR = "/@oh-images/";
|
|
92
|
+
function ohImage(options) {
|
|
93
|
+
let isBuild = false;
|
|
94
|
+
let assetsDir;
|
|
95
|
+
let outDir;
|
|
96
|
+
let cacheDir;
|
|
97
|
+
const imageEntries = /* @__PURE__ */ new Map();
|
|
98
|
+
const config = {
|
|
99
|
+
...DEFAULT_CONFIGS,
|
|
100
|
+
...options
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* used for dev server to match url to path
|
|
104
|
+
* @param url
|
|
105
|
+
*/
|
|
106
|
+
function urlToPath(url) {
|
|
107
|
+
const fileId = basename(url);
|
|
108
|
+
return join(cacheDir, fileId);
|
|
109
|
+
}
|
|
110
|
+
function genIdentifier(uri, format, prefix) {
|
|
111
|
+
const fileId = basename(uri);
|
|
112
|
+
const uniqueFileId = `${prefix}-${getRandomString()}-${fileId}.${format}`;
|
|
113
|
+
if (!isBuild) return join(DEV_DIR, uniqueFileId);
|
|
114
|
+
return join(assetsDir, config.distDir, uniqueFileId);
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
name: "oh-image",
|
|
118
|
+
configResolved(viteConfig) {
|
|
119
|
+
cacheDir = join(viteConfig.cacheDir, DEV_DIR);
|
|
120
|
+
isBuild = viteConfig.command === "build";
|
|
121
|
+
assetsDir = viteConfig.build.assetsDir;
|
|
122
|
+
outDir = join(viteConfig.root, viteConfig.build.outDir);
|
|
123
|
+
},
|
|
124
|
+
enforce: "pre",
|
|
125
|
+
configureServer(server) {
|
|
126
|
+
server.middlewares.use(async (req, res, next) => {
|
|
127
|
+
const url = req.url;
|
|
128
|
+
if (!url?.includes(DEV_DIR) || !SUPPORTED_IMAGE_FORMATS.test(url)) return next();
|
|
129
|
+
const path = urlToPath(url);
|
|
130
|
+
const ext = extname(url).slice(1);
|
|
131
|
+
const image = await readFileSafe(path);
|
|
132
|
+
const imageEntry = imageEntries.get(url);
|
|
133
|
+
if (!imageEntry) {
|
|
134
|
+
console.warn("Image entry not found with id: " + url);
|
|
135
|
+
next();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (image) {
|
|
139
|
+
res.setHeader("Content-Type", `image/${ext}`);
|
|
140
|
+
res.end(image);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const processed = await processImage(imageEntry.origin, imageEntry);
|
|
144
|
+
await saveFileSafe(path, processed);
|
|
145
|
+
res.setHeader("Content-Type", `image/${ext}`);
|
|
146
|
+
res.end(processed);
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
load: {
|
|
150
|
+
filter: { id: SUPPORTED_IMAGE_FORMATS },
|
|
151
|
+
async handler(id) {
|
|
152
|
+
try {
|
|
153
|
+
const parsed = queryToOptions(PROCESS_KEY, id);
|
|
154
|
+
if (!parsed.shouldProcess) return null;
|
|
155
|
+
const origin = parsed.path;
|
|
156
|
+
const { name, ext } = parse(parsed.path);
|
|
157
|
+
const metadata = await sharp(parsed.path).metadata();
|
|
158
|
+
const mergedOptions = {
|
|
159
|
+
...config,
|
|
160
|
+
...parsed.options
|
|
161
|
+
};
|
|
162
|
+
const mainIdentifier = genIdentifier(name, mergedOptions.format ?? ext.slice(1), "main");
|
|
163
|
+
const mainEntry = {
|
|
164
|
+
width: mergedOptions.width,
|
|
165
|
+
height: mergedOptions.height,
|
|
166
|
+
blur: mergedOptions.blur,
|
|
167
|
+
format: mergedOptions.format,
|
|
168
|
+
origin
|
|
169
|
+
};
|
|
170
|
+
imageEntries.set(mainIdentifier, mainEntry);
|
|
171
|
+
const src = {
|
|
172
|
+
width: metadata.width,
|
|
173
|
+
height: metadata.height,
|
|
174
|
+
src: mainIdentifier,
|
|
175
|
+
srcSets: []
|
|
176
|
+
};
|
|
177
|
+
if (parsed.options?.placeholder) {
|
|
178
|
+
const placeholderIdentifier = genIdentifier(name, mergedOptions.placeholderF, "placeholder");
|
|
179
|
+
const placeholderEntry = {
|
|
180
|
+
width: mergedOptions.placeholderW,
|
|
181
|
+
height: mergedOptions.placeholderH,
|
|
182
|
+
format: mergedOptions.placeholderF,
|
|
183
|
+
blur: mergedOptions.placeholderB,
|
|
184
|
+
origin
|
|
185
|
+
};
|
|
186
|
+
imageEntries.set(placeholderIdentifier, placeholderEntry);
|
|
187
|
+
src.placeholderUrl = placeholderIdentifier;
|
|
188
|
+
}
|
|
189
|
+
if (mergedOptions.bps) for (const breakpoint of mergedOptions.bps) {
|
|
190
|
+
const srcSetIdentifier = genIdentifier(name, mergedOptions.srcSetsF, `breakpoint-${breakpoint}`);
|
|
191
|
+
const srcSetEntry = {
|
|
192
|
+
width: breakpoint,
|
|
193
|
+
format: mergedOptions.srcSetsF,
|
|
194
|
+
origin
|
|
195
|
+
};
|
|
196
|
+
imageEntries.set(srcSetIdentifier, srcSetEntry);
|
|
197
|
+
src.srcSets.push({
|
|
198
|
+
src: srcSetIdentifier,
|
|
199
|
+
width: `${breakpoint}w`
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
return `export default ${JSON.stringify(src)};`;
|
|
203
|
+
} catch (err) {
|
|
204
|
+
console.error(`Couldn't load image with id: ${id} error:${err}`);
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
async writeBundle() {
|
|
210
|
+
const limit = pLimit(30);
|
|
211
|
+
const tasks = Array.from(imageEntries, ([key, value]) => limit(async () => {
|
|
212
|
+
const processed = await processImage(value.origin, value);
|
|
213
|
+
await saveFileSafe(join(outDir, key), processed);
|
|
214
|
+
}));
|
|
215
|
+
await Promise.all(tasks);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
//#endregion
|
|
221
|
+
export { ohImage };
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
2
|
+
import { CSSProperties } from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/react/types.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Image source type - can be either a simple URL string or a full ImageSrc object
|
|
7
|
+
*/
|
|
8
|
+
type ImageSrcType = string | ImageSrc;
|
|
9
|
+
/**
|
|
10
|
+
* Optimized image source with multiple responsive variants
|
|
11
|
+
*/
|
|
12
|
+
interface ImageSrc {
|
|
13
|
+
/** Original image width in pixels */
|
|
14
|
+
width: number;
|
|
15
|
+
/** Original image height in pixels */
|
|
16
|
+
height: number;
|
|
17
|
+
/** Optional low-quality placeholder image URL for blur-up effect */
|
|
18
|
+
placeholderUrl?: string;
|
|
19
|
+
/** Array of responsive image variants for different screen sizes */
|
|
20
|
+
srcSets: ImageSrcSet[];
|
|
21
|
+
/** Primary image source URL */
|
|
22
|
+
src: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Single responsive image variant in a srcset
|
|
26
|
+
*/
|
|
27
|
+
interface ImageSrcSet {
|
|
28
|
+
/** Width descriptor (e.g., "1920w") */
|
|
29
|
+
width: string;
|
|
30
|
+
/** Image URL for this variant */
|
|
31
|
+
src: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Props for the optimized Image component
|
|
35
|
+
* Extends standard HTML image attributes with optimization features
|
|
36
|
+
*/
|
|
37
|
+
interface ImageProps extends Partial<Pick<HTMLImageElement, "alt" | "fetchPriority" | "decoding" | "loading" | "height" | "width" | "srcset" | "className" | "sizes">> {
|
|
38
|
+
/** Load the image immediately, bypassing lazy loading */
|
|
39
|
+
asap?: boolean;
|
|
40
|
+
/** Image source - either a URL string or ImageSrc object with responsive variants */
|
|
41
|
+
src: ImageSrcType;
|
|
42
|
+
/** Override placeholder URL (takes precedence over ImageSrc.placeholderUrl) */
|
|
43
|
+
placeholderUrl?: string | undefined;
|
|
44
|
+
/** Enable blur-up placeholder effect during image loading */
|
|
45
|
+
placeholder?: boolean;
|
|
46
|
+
/** Inline CSS styles */
|
|
47
|
+
style?: CSSProperties;
|
|
48
|
+
/** Make image fill its container (position: absolute) */
|
|
49
|
+
fill?: boolean;
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/react/image.d.ts
|
|
53
|
+
declare function Image(props: ImageProps): react_jsx_runtime0.JSX.Element;
|
|
54
|
+
//#endregion
|
|
55
|
+
export { Image, type ImageProps, type ImageSrc, type ImageSrcSet };
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { preload } from "react-dom";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
|
|
4
|
+
//#region src/react/image.tsx
|
|
5
|
+
function resolveOptions(props) {
|
|
6
|
+
const { src, ...rest } = props;
|
|
7
|
+
const resolved = { ...rest };
|
|
8
|
+
if (typeof src === "object") {
|
|
9
|
+
resolved.src = src.src;
|
|
10
|
+
resolved.width ??= src.width;
|
|
11
|
+
resolved.height ??= src.height;
|
|
12
|
+
resolved.srcset ??= src.srcSets.map((set) => `${set.src} ${set.width}`).join(", ");
|
|
13
|
+
resolved.placeholderUrl ??= src.placeholderUrl;
|
|
14
|
+
} else resolved.src = src;
|
|
15
|
+
if (props.asap) {
|
|
16
|
+
resolved.decoding = "async";
|
|
17
|
+
resolved.loading = "eager";
|
|
18
|
+
resolved.fetchPriority = "high";
|
|
19
|
+
preload(resolved.src, {
|
|
20
|
+
as: "image",
|
|
21
|
+
fetchPriority: "high"
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
if (props.fill) resolved.sizes ||= "100vw";
|
|
25
|
+
return resolved;
|
|
26
|
+
}
|
|
27
|
+
function getPlaceholderStyles(props) {
|
|
28
|
+
if (!props.placeholder) return {};
|
|
29
|
+
if (!props.placeholderUrl) {
|
|
30
|
+
console.warn("Blur URL is required for placeholder");
|
|
31
|
+
return {};
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
backgroundPosition: "50% 50%",
|
|
35
|
+
backgroundRepeat: "no-repeat",
|
|
36
|
+
backgroundImage: `url(${props.placeholderUrl})`,
|
|
37
|
+
backgroundSize: "cover"
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function getFillStyles(props) {
|
|
41
|
+
if (!props.fill) return {};
|
|
42
|
+
return {
|
|
43
|
+
width: "100%",
|
|
44
|
+
height: "100%",
|
|
45
|
+
inset: "0"
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function Image(props) {
|
|
49
|
+
const options = resolveOptions(props);
|
|
50
|
+
const placeholderStyles = getPlaceholderStyles(options);
|
|
51
|
+
const fillStyles = getFillStyles(options);
|
|
52
|
+
const styles = {
|
|
53
|
+
...placeholderStyles,
|
|
54
|
+
...fillStyles,
|
|
55
|
+
...props.style
|
|
56
|
+
};
|
|
57
|
+
return /* @__PURE__ */ jsx("img", {
|
|
58
|
+
className: props.className,
|
|
59
|
+
style: styles,
|
|
60
|
+
src: options.src,
|
|
61
|
+
width: options.width,
|
|
62
|
+
height: options.height,
|
|
63
|
+
srcSet: options.srcset,
|
|
64
|
+
alt: options.alt,
|
|
65
|
+
loading: options.loading,
|
|
66
|
+
decoding: options.decoding,
|
|
67
|
+
sizes: options.sizes,
|
|
68
|
+
fetchPriority: options.fetchPriority ?? "auto"
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
//#endregion
|
|
73
|
+
export { Image };
|
package/package.json
CHANGED
|
@@ -1,37 +1,74 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lonik/oh-image",
|
|
3
|
-
"version": "0.0.1",
|
|
4
3
|
"type": "module",
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
"version": "1.0.2",
|
|
5
|
+
"description": "A React component library for optimized image handling.",
|
|
6
|
+
"author": "Luka Onikadze <lukonik@gmail.com>",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://github.com/lukonik/oh-image#readme",
|
|
8
9
|
"repository": {
|
|
9
10
|
"type": "git",
|
|
10
|
-
"url": "https://github.com/lukonik/oh-image.git"
|
|
11
|
-
|
|
11
|
+
"url": "git+https://github.com/lukonik/oh-image.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/lukonik/oh-image/issues"
|
|
12
15
|
},
|
|
13
|
-
"keywords": [
|
|
14
|
-
"image",
|
|
15
|
-
"optimization",
|
|
16
|
-
"processing"
|
|
17
|
-
],
|
|
18
|
-
"main": "./dist/index.js",
|
|
19
|
-
"module": "./dist/index.js",
|
|
20
|
-
"types": "./dist/index.d.ts",
|
|
21
16
|
"exports": {
|
|
22
17
|
"./package.json": "./package.json",
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
"./plugin": {
|
|
19
|
+
"types": "./dist/plugin.d.ts",
|
|
20
|
+
"default": "./dist/plugin.js"
|
|
21
|
+
},
|
|
22
|
+
"./react": {
|
|
23
|
+
"types": "./dist/react.d.ts",
|
|
24
|
+
"default": "./dist/react.js"
|
|
28
25
|
}
|
|
29
26
|
},
|
|
30
27
|
"files": [
|
|
31
|
-
"dist"
|
|
32
|
-
"!**/*.tsbuildinfo"
|
|
28
|
+
"dist"
|
|
33
29
|
],
|
|
34
|
-
"
|
|
35
|
-
"
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsdown",
|
|
35
|
+
"dev": "tsdown --watch",
|
|
36
|
+
"play": "vite",
|
|
37
|
+
"play:build": "vite build",
|
|
38
|
+
"play:preview": "vite preview",
|
|
39
|
+
"test": "vitest",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"release": "bumpp && pnpm publish",
|
|
42
|
+
"prepublishOnly": "pnpm run build"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"react": "^19.2.0",
|
|
46
|
+
"react-dom": "^19.2.0",
|
|
47
|
+
"sharp": "^0.34.5"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@eslint/js": "^9.39.2",
|
|
51
|
+
"@tsconfig/strictest": "^2.0.8",
|
|
52
|
+
"@types/node": "^25.0.3",
|
|
53
|
+
"@types/react": "^19.2.7",
|
|
54
|
+
"@types/react-dom": "^19.2.3",
|
|
55
|
+
"@vitejs/plugin-react": "^5.1.2",
|
|
56
|
+
"@vitest/browser-playwright": "^4.0.16",
|
|
57
|
+
"bumpp": "^10.3.2",
|
|
58
|
+
"eslint": "^9.39.2",
|
|
59
|
+
"eslint-config-prettier": "^10.1.8",
|
|
60
|
+
"eslint-plugin-react": "^7.37.5",
|
|
61
|
+
"globals": "^17.3.0",
|
|
62
|
+
"tsdown": "^0.18.1",
|
|
63
|
+
"typescript": "^5.9.3",
|
|
64
|
+
"typescript-eslint": "^8.54.0",
|
|
65
|
+
"vite": "^7.3.0",
|
|
66
|
+
"vitest": "^4.0.16",
|
|
67
|
+
"vitest-browser-react": "^2.0.2",
|
|
68
|
+
"@commitlint/config-conventional": "^20.4.1"
|
|
69
|
+
},
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"p-limit": "^7.3.0",
|
|
72
|
+
"query-string": "^9.3.1"
|
|
36
73
|
}
|
|
37
|
-
}
|
|
74
|
+
}
|