@pixelfiddler/core 0.1.1 → 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 +2 -2
- package/dist/index.cjs +116 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -2
- package/dist/index.d.ts +80 -2
- package/dist/index.js +116 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,8 +77,8 @@ Converts transformation options to a URL-encoded query string.
|
|
|
77
77
|
|
|
78
78
|
```typescript
|
|
79
79
|
{
|
|
80
|
-
format: 'webp', // '
|
|
81
|
-
quality:
|
|
80
|
+
format: 'webp', // 'jpg' | 'png' | 'webp' | 'avif' | 'gif' | 'auto'
|
|
81
|
+
quality: 80, // 1-100
|
|
82
82
|
stripMetadata: true // Remove EXIF data
|
|
83
83
|
}
|
|
84
84
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,112 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// src/src-set.ts
|
|
4
|
+
var MAX_IMAGE_WIDTH = 4096;
|
|
5
|
+
var DEFAULT_DEVICE_BREAKPOINTS = [
|
|
6
|
+
320,
|
|
7
|
+
375,
|
|
8
|
+
640,
|
|
9
|
+
768,
|
|
10
|
+
1024,
|
|
11
|
+
1280,
|
|
12
|
+
1536,
|
|
13
|
+
1920
|
|
14
|
+
];
|
|
15
|
+
var DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 384];
|
|
16
|
+
var DEFAULT_MAX_DPR = 2;
|
|
17
|
+
function computeCandidateWidths(params) {
|
|
18
|
+
const {
|
|
19
|
+
allBreakpoints,
|
|
20
|
+
deviceBreakpoints,
|
|
21
|
+
explicitWidth,
|
|
22
|
+
sizesAttr
|
|
23
|
+
} = params;
|
|
24
|
+
const sortedAll = [...new Set(allBreakpoints)].sort((a, b) => a - b);
|
|
25
|
+
const sortedDevice = [...new Set(deviceBreakpoints)].sort((a, b) => a - b);
|
|
26
|
+
if (!sortedAll.length || !sortedDevice.length) {
|
|
27
|
+
return { candidates: [], descriptor: "w" };
|
|
28
|
+
}
|
|
29
|
+
const minDevice = sortedDevice[0];
|
|
30
|
+
const maxDevice = sortedDevice[sortedDevice.length - 1];
|
|
31
|
+
const vwRatios = parseVW(sizesAttr);
|
|
32
|
+
const hasVW = vwRatios.length > 0;
|
|
33
|
+
if (typeof explicitWidth === "number" && !hasVW) {
|
|
34
|
+
const DPRs = [1, 2, 3];
|
|
35
|
+
const candidates = Array.from(
|
|
36
|
+
new Set(
|
|
37
|
+
DPRs.map((dpr) => nearest(sortedAll, explicitWidth * dpr)).map(clampMax)
|
|
38
|
+
)
|
|
39
|
+
).filter((w) => w <= MAX_IMAGE_WIDTH);
|
|
40
|
+
return {
|
|
41
|
+
candidates,
|
|
42
|
+
descriptor: "x"
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (hasVW) {
|
|
46
|
+
const minRatio = Math.min(...vwRatios);
|
|
47
|
+
const maxRatio = Math.max(...vwRatios);
|
|
48
|
+
const minPx = minDevice * minRatio;
|
|
49
|
+
const maxPx = maxDevice * maxRatio * DEFAULT_MAX_DPR;
|
|
50
|
+
const candidates = sortedAll.filter(
|
|
51
|
+
(w) => w >= minPx && w <= clampMax(maxPx)
|
|
52
|
+
);
|
|
53
|
+
return {
|
|
54
|
+
candidates,
|
|
55
|
+
descriptor: "w"
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const maxUseful = maxDevice * DEFAULT_MAX_DPR;
|
|
59
|
+
return {
|
|
60
|
+
candidates: sortedAll.filter((w) => w <= clampMax(maxUseful)),
|
|
61
|
+
descriptor: "w"
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
var createResponsiveAttributes = (config, options = {}) => {
|
|
65
|
+
const {
|
|
66
|
+
sizes,
|
|
67
|
+
width,
|
|
68
|
+
deviceBreakpoints = [...DEFAULT_DEVICE_BREAKPOINTS],
|
|
69
|
+
imageBreakpoints = [...DEFAULT_IMAGE_BREAKPOINTS],
|
|
70
|
+
transformations = {},
|
|
71
|
+
mode = "short"
|
|
72
|
+
} = options;
|
|
73
|
+
const sortedDeviceBreakpoints = [...deviceBreakpoints].sort((a, b) => a - b);
|
|
74
|
+
const sortedImageBreakpoints = [...imageBreakpoints].sort((a, b) => a - b);
|
|
75
|
+
const allBreakpoints = [.../* @__PURE__ */ new Set([...sortedImageBreakpoints, ...sortedDeviceBreakpoints])].sort((a, b) => a - b);
|
|
76
|
+
const { candidates, descriptor } = computeCandidateWidths({
|
|
77
|
+
allBreakpoints,
|
|
78
|
+
deviceBreakpoints: sortedDeviceBreakpoints,
|
|
79
|
+
...width !== void 0 && { explicitWidth: width },
|
|
80
|
+
...sizes !== void 0 && { sizesAttr: sizes }
|
|
81
|
+
});
|
|
82
|
+
if (candidates.length === 0) {
|
|
83
|
+
return { src: config.baseUrl };
|
|
84
|
+
}
|
|
85
|
+
const srcSetEntries = candidates.map((w, index) => buildSrcEntries(w, index, config, transformations, mode, descriptor));
|
|
86
|
+
const largestWidth = candidates[candidates.length - 1];
|
|
87
|
+
const src = buildTransformationUrl(
|
|
88
|
+
config,
|
|
89
|
+
{ ...transformations, width: largestWidth },
|
|
90
|
+
mode
|
|
91
|
+
);
|
|
92
|
+
const endSizes = sizes ?? (descriptor === "w" ? "100vw" : void 0);
|
|
93
|
+
return {
|
|
94
|
+
src,
|
|
95
|
+
...endSizes ? { sizes: endSizes } : {},
|
|
96
|
+
srcSet: srcSetEntries.join(", "),
|
|
97
|
+
...width !== void 0 ? { width } : {}
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
function buildSrcEntries(width, index, config, transformations, mode, descriptor) {
|
|
101
|
+
const url = buildTransformationUrl(
|
|
102
|
+
config,
|
|
103
|
+
{ ...transformations, width },
|
|
104
|
+
mode
|
|
105
|
+
);
|
|
106
|
+
const desc = descriptor === "w" ? `${width}w` : `${index + 1}x`;
|
|
107
|
+
return `${url} ${desc}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
3
110
|
// src/utils.ts
|
|
4
111
|
var normalizeHex = (value) => value.replace("#", "");
|
|
5
112
|
var removeTrailingSlash = (baseUrl) => {
|
|
@@ -9,6 +116,14 @@ var getNestedValue = (obj, path) => path.reduce(
|
|
|
9
116
|
(acc, key) => acc == null ? void 0 : acc[key],
|
|
10
117
|
obj
|
|
11
118
|
);
|
|
119
|
+
var clampMax = (v) => Math.min(v, MAX_IMAGE_WIDTH);
|
|
120
|
+
var nearest = (sortedBreakpoints, target) => sortedBreakpoints.find((w) => w >= target) ?? sortedBreakpoints[sortedBreakpoints.length - 1];
|
|
121
|
+
var parseVW = (sizes) => {
|
|
122
|
+
if (!sizes) return [];
|
|
123
|
+
return [...sizes.matchAll(/(\d+(?:\.\d+)?)vw/g)].map(
|
|
124
|
+
(m) => Number(m[1]) / 100
|
|
125
|
+
);
|
|
126
|
+
};
|
|
12
127
|
|
|
13
128
|
// src/config.ts
|
|
14
129
|
var PARAMS = [
|
|
@@ -105,5 +220,6 @@ var buildTransformationUrl = (config, options, mode = "short") => {
|
|
|
105
220
|
|
|
106
221
|
exports.buildTransformationQueryParams = buildTransformationQueryParams;
|
|
107
222
|
exports.buildTransformationUrl = buildTransformationUrl;
|
|
223
|
+
exports.createResponsiveAttributes = createResponsiveAttributes;
|
|
108
224
|
//# sourceMappingURL=index.cjs.map
|
|
109
225
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils.ts","../src/config.ts","../src/url-builder.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import { Path, PathValue } from './util-types';\r\n\r\nexport const normalizeHex = (value: string) => value.replace('#', '')\r\n\r\n\r\nexport const removeTrailingSlash = (baseUrl: string) => {\r\n return baseUrl.endsWith('/')\r\n ? baseUrl.slice(0, -1)\r\n : baseUrl;\r\n}\r\n\r\nexport const getNestedValue = <\r\n T,\r\n P extends Path<T>\r\n>(\r\n obj: T,\r\n path: P\r\n): PathValue<T, P> | undefined =>\r\n path.reduce<any>(\r\n (acc, key) => (acc == null ? undefined : acc[key]),\r\n obj\r\n );\r\n\r\n","import { ParamSpec, Path } from './util-types';\r\nimport { TransformationOptions } from './types';\r\nimport { normalizeHex } from './utils';\r\n\r\nexport const PARAMS = [\r\n {path: ['width'], long: 'width', short: 'w'},\r\n {path: ['height'], long: 'height', short: 'h'},\r\n {path: ['dpr'], long: 'dpr', short: 'dpr'},\r\n {path: ['mode'], long: 'mode', short: 'rm'},\r\n {\r\n path: ['background'],\r\n long: 'background',\r\n short: 'bg',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n\r\n {path: ['format'], long: 'format', short: 'f'},\r\n {path: ['quality'], long: 'quality', short: 'q'},\r\n {path: ['stripMetadata'], long: 'stripMetadata', short: 'st'},\r\n\r\n {path: ['blur'], long: 'blur', short: 'bl'},\r\n {path: ['brightness'], long: 'brightness', short: 'br'},\r\n {path: ['contrast'], long: 'contrast', short: 'ct'},\r\n {path: ['grayscale'], long: 'grayscale', short: 'gs'},\r\n {path: ['saturation'], long: 'saturation', short: 'sa'},\r\n {path: ['sepia'], long: 'sepia', short: 'sp'},\r\n {path: ['sharpen'], long: 'sharpen', short: 'sh'},\r\n {path: ['noise'], long: 'noise', short: 'ns'},\r\n {path: ['rotate'], long: 'rotate', short: 'rt'},\r\n\r\n {path: ['border', 'width'], long: 'border.width', short: 'bo.w'},\r\n {\r\n path: ['border', 'color'],\r\n long: 'border.color',\r\n short: 'bo.c',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['border', 'radius'], long: 'border.radius', short: 'bo.r'},\r\n\r\n {path: ['crop', 'type'], long: 'crop.type', short: 'cr.t'},\r\n {path: ['crop', 'objectType'], long: 'crop.objectType', short: 'cr.o'},\r\n {path: ['crop', 'focus', 'position', 'x'], long: 'crop.x', short: 'cr.x'},\r\n {path: ['crop', 'focus', 'position', 'y'], long: 'crop.y', short: 'cr.y'},\r\n {path: ['crop', 'focus', 'strategy'], long: 'crop', short: 'cr'},\r\n {path: ['crop', 'width'], long: 'crop.width', short: 'cr.w'},\r\n {path: ['crop', 'height'], long: 'crop.height', short: 'cr.h'},\r\n {path: ['crop', 'afterResize'], long: 'crop.afterResize', short: 'cr.ar'},\r\n\r\n {path: ['text', 'text'], long: 'text', short: 'tx'},\r\n {\r\n path: ['text', 'color'],\r\n long: 'text.color',\r\n short: 'tx.c',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['text', 'opacity'], long: 'text.opacity', short: 'tx.o'},\r\n {path: ['text', 'size'], long: 'text.size', short: 'tx.s'},\r\n {path: ['text', 'font'], long: 'text.font', short: 'tx.f'},\r\n {path: ['text', 'fontSize'], long: 'text.fontSize', short: 'tx.fs'},\r\n {path: ['text', 'position'], long: 'text.position', short: 'tx.p'},\r\n {\r\n path: ['text', 'background'],\r\n long: 'text.background',\r\n short: 'tx.bg',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['text', 'backgroundOpacity'], long: 'text.backgroundOpacity', short: 'tx.bg.o'},\r\n\r\n {path: ['watermark', 'name'], long: 'watermark', short: 'wm'},\r\n {path: ['watermark', 'opacity'], long: 'watermark.opacity', short: 'wm.o'},\r\n {path: ['watermark', 'size'], long: 'watermark.size', short: 'wm.s'},\r\n {path: ['watermark', 'width'], long: 'watermark.width', short: 'wm.w'},\r\n {path: ['watermark', 'height'], long: 'watermark.height', short: 'wm.h'},\r\n {path: ['watermark', 'position'], long: 'watermark.position', short: 'wm.p'},\r\n] as const satisfies readonly ParamSpec<\r\n TransformationOptions,\r\n Path<TransformationOptions>\r\n>[];\r\n","import { TransformationOptions, UrlBuilderConfig } from './types';\r\nimport { getNestedValue, removeTrailingSlash } from './utils';\r\nimport { PARAMS } from './config';\r\n\r\n/**\r\n * Determines which parameter names to use in the query string.\r\n * - `'short'` - Use abbreviated aliases (e.g., `w`, `h`, `q`)\r\n * - `'long'` - Use full parameter names (e.g., `width`, `height`, `quality`)\r\n */\r\ntype NameMode = 'short' | 'long';\r\n\r\n/**\r\n * Converts transformation options into a URL-encoded query string.\r\n *\r\n * Iterates through all defined parameters in the PARAMS config, extracts values\r\n * from the options object using their paths, applies any normalization functions\r\n * (e.g., stripping `#` from hex colors), and returns a URL-encoded query string.\r\n *\r\n * @param options - The transformation options object\r\n * @param mode - Whether to use short aliases or long parameter names (default: `'short'`)\r\n * @returns URL-encoded query string (without leading `?`)\r\n *\r\n * @example\r\n * ```ts\r\n * // Using short aliases (default)\r\n * buildQueryParams({ width: 800, height: 600, quality: 80 });\r\n * // => 'w=800&h=600&q=80'\r\n *\r\n * // Using long parameter names\r\n * buildQueryParams({ width: 800, format: 'webp' }, 'long');\r\n * // => 'width=800&format=webp'\r\n *\r\n * // Nested options\r\n * buildQueryParams({ border: { width: 2, color: '#ff0000' } });\r\n * // => 'bo.w=2&bo.c=ff0000'\r\n * ```\r\n */\r\nexport const buildTransformationQueryParams = (\r\n options: TransformationOptions,\r\n mode: NameMode = 'short'\r\n): string => {\r\n const params: Array<[string, string]> = [];\r\n\r\n for (const spec of PARAMS) {\r\n const value = getNestedValue(options, spec.path);\r\n if (value == null) continue;\r\n\r\n const name =\r\n mode === 'short' && spec.short ? spec.short : spec.long;\r\n\r\n let normalized = value\r\n if ('normalize' in spec) {\r\n normalized = spec.normalize(value)\r\n }\r\n\r\n params.push([name, String(normalized)]);\r\n }\r\n\r\n\r\n return new URLSearchParams(\r\n params.map(([k, v]) => [k, v])\r\n ).toString();\r\n\r\n};\r\n\r\n/**\r\n * Builds a complete transformation URL from a base URL and transformation options.\r\n *\r\n * Combines the configured base URL with query parameters generated from the\r\n * transformation options. Handles the special `original` flag which returns\r\n * the unmodified source image.\r\n *\r\n * @param config - Configuration containing the base URL for the image\r\n * @param options - The transformation options to apply\r\n * @param mode - Whether to use short aliases or long parameter names (default: `'short'`)\r\n * @returns The complete URL string with query parameters\r\n *\r\n * @example\r\n * ```ts\r\n * const config = { baseUrl: 'https://example.com/images/photo.jpg' };\r\n *\r\n * // Basic resize\r\n * buildTransformationUrl(config, { width: 400, height: 300 });\r\n * // => 'https://example.com/images/photo.jpg?w=400&h=300'\r\n *\r\n * // Multiple transformations\r\n * buildTransformationUrl(config, {\r\n * width: 800,\r\n * format: 'webp',\r\n * quality: 85,\r\n * blur: 5\r\n * });\r\n * // => 'https://example.com/images/photo.jpg?w=800&f=webp&q=85&bl=5'\r\n *\r\n * // Request original image (no transformations)\r\n * buildTransformationUrl(config, { original: true });\r\n * // => 'https://example.com/images/photo.jpg?original'\r\n *\r\n * // Using long parameter names\r\n * buildTransformationUrl(config, { width: 400 }, 'long');\r\n * // => 'https://example.com/images/photo.jpg?width=400'\r\n * ```\r\n */\r\nexport const buildTransformationUrl = (\r\n config: UrlBuilderConfig,\r\n options: TransformationOptions,\r\n mode: NameMode = 'short'\r\n) => {\r\n if (options.original) {\r\n return `${config.baseUrl}?original`;\r\n }\r\n\r\n const query = buildTransformationQueryParams(options, mode);\r\n\r\n const baseUrl = removeTrailingSlash(config.baseUrl)\r\n return `${baseUrl}?${query}`;\r\n};\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/src-set.ts","../src/utils.ts","../src/config.ts","../src/url-builder.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import { buildTransformationUrl, NameMode } from './url-builder';\r\nimport { TransformationOptions, UrlBuilderConfig } from './types';\r\nimport { clampMax, nearest, parseVW } from './utils';\r\n\r\n/**\r\n * Maximum supported image width\r\n */\r\nexport const MAX_IMAGE_WIDTH = 4096;\r\n\r\n/**\r\n * Default device breakpoints for responsive srcset generation\r\n */\r\nexport const DEFAULT_DEVICE_BREAKPOINTS = [\r\n 320,\r\n 375,\r\n 640,\r\n 768,\r\n 1024,\r\n 1280,\r\n 1536,\r\n 1920\r\n] as const;\r\n\r\n/**\r\n * Default image breakpoints for smaller images (icons, thumbnails)\r\n */\r\nexport const DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 384] as const;\r\n\r\n/**\r\n * Options for responsive image attributes generation\r\n */\r\nexport interface ResponsiveImageOptions {\r\n /**\r\n * The `sizes` attribute value (e.g., \"100vw\", \"(max-width: 768px) 100vw, 50vw\").\r\n * When provided, generates width descriptors (`w`) for responsive layouts.\r\n */\r\n sizes?: string;\r\n /**\r\n * Fixed display width in CSS pixels.\r\n * When provided without `sizes`, generates DPR descriptors (`x`).\r\n */\r\n width?: number;\r\n /** Device breakpoints for viewport-based sizing (default: DEFAULT_DEVICE_BREAKPOINTS) */\r\n deviceBreakpoints?: number[];\r\n /** Image breakpoints for smaller images (default: DEFAULT_IMAGE_BREAKPOINTS) */\r\n imageBreakpoints?: number[];\r\n /** Base transformation options to apply to all variants */\r\n transformations?: TransformationOptions;\r\n /** Whether to use short or long parameter names (default: 'short') */\r\n mode?: NameMode;\r\n}\r\n\r\n/**\r\n * Result of responsive image attributes generation\r\n */\r\nexport interface ResponsiveImageResult {\r\n /** The src attribute value (largest/default variant) */\r\n src: string;\r\n /** The srcset attribute value */\r\n srcSet?: string;\r\n /** The sizes attribute value (for width descriptors) */\r\n sizes?: string;\r\n /** The width attribute value (when explicitly provided) */\r\n width?: number;\r\n}\r\n\r\n/**\r\n * Computes candidate widths and descriptor type based on input options.\r\n */\r\nconst DEFAULT_MAX_DPR = 2;\r\n\r\nfunction computeCandidateWidths(params: {\r\n allBreakpoints: number[];\r\n deviceBreakpoints: number[];\r\n explicitWidth?: number;\r\n sizesAttr?: string;\r\n}): { candidates: number[]; descriptor: 'w' | 'x' } {\r\n const {\r\n allBreakpoints,\r\n deviceBreakpoints,\r\n explicitWidth,\r\n sizesAttr\r\n } = params;\r\n\r\n // ---- Normalize & guard ----\r\n const sortedAll = [...new Set(allBreakpoints)].sort((a, b) => a - b);\r\n const sortedDevice = [...new Set(deviceBreakpoints)].sort((a, b) => a - b);\r\n\r\n if (!sortedAll.length || !sortedDevice.length) {\r\n return { candidates: [], descriptor: 'w' };\r\n }\r\n\r\n const minDevice = sortedDevice[0]!;\r\n const maxDevice = sortedDevice[sortedDevice.length - 1]!;\r\n\r\n const vwRatios = parseVW(sizesAttr);\r\n const hasVW = vwRatios.length > 0;\r\n\r\n // Explicit width (fixed layout) → x descriptors ----\r\n // Prefer this when layout width is known and sizes does NOT imply vw scaling\r\n if (typeof explicitWidth === 'number' && !hasVW) {\r\n const DPRs = [1, 2, 3];\r\n\r\n const candidates = Array.from(\r\n new Set(\r\n DPRs\r\n .map(dpr => nearest(sortedAll, explicitWidth * dpr))\r\n .map(clampMax)\r\n )\r\n ).filter(w => w <= MAX_IMAGE_WIDTH);\r\n\r\n return {\r\n candidates,\r\n descriptor: 'x'\r\n };\r\n }\r\n\r\n // Sizes with vw → w descriptors ----\r\n if (hasVW) {\r\n const minRatio = Math.min(...vwRatios);\r\n const maxRatio = Math.max(...vwRatios);\r\n\r\n const minPx = minDevice * minRatio;\r\n const maxPx = maxDevice * maxRatio * DEFAULT_MAX_DPR;\r\n\r\n const candidates = sortedAll.filter(\r\n w => w >= minPx && w <= clampMax(maxPx)\r\n );\r\n\r\n return {\r\n candidates,\r\n descriptor: 'w'\r\n };\r\n }\r\n\r\n // No sizes & no explicit width → device-based w ----\r\n const maxUseful = maxDevice * DEFAULT_MAX_DPR;\r\n\r\n return {\r\n candidates: sortedAll.filter(w => w <= clampMax(maxUseful)),\r\n descriptor: 'w'\r\n };\r\n}\r\n\r\n/**\r\n * Generates responsive image attributes (src, srcSet, sizes) for optimal loading.\r\n *\r\n * Supports two strategies:\r\n * 1. **Responsive (with `sizes`)**: Uses width descriptors (`w`) for fluid layouts\r\n * where image size varies with viewport.\r\n * 2. **Fixed width (with `width`)**: Uses DPR descriptors (`x`) for fixed-size\r\n * images that need high-DPI support.\r\n *\r\n * @param config - Configuration containing the base URL for the image\r\n * @param options - Options for responsive image generation\r\n * @returns Object containing src, srcSet, and optionally sizes attributes\r\n *\r\n * @example\r\n * ```ts\r\n * const config = { baseUrl: 'https://example.com/photo.jpg' };\r\n *\r\n * // Responsive image with sizes attribute (uses `w` descriptors)\r\n * createResponsiveAttributes(config, { sizes: '(max-width: 768px) 100vw, 50vw' });\r\n * // => {\r\n * // src: '...?w=3840',\r\n * // srcSet: '...?w=640 640w, ...?w=750 750w, ..., ...?w=3840 3840w',\r\n * // sizes: '(max-width: 768px) 100vw, 50vw'\r\n * // }\r\n *\r\n * // Fixed-width image (uses `x` descriptors)\r\n * createResponsiveAttributes(config, { width: 400 });\r\n * // => {\r\n * // src: '...?w=384',\r\n * // srcSet: '...?w=384 1x, ...?w=828 2x',\r\n * // width: 400\r\n * // }\r\n *\r\n * // With transformations\r\n * createResponsiveAttributes(config, {\r\n * sizes: '100vw',\r\n * transformations: { format: 'WEBP', quality: 80 }\r\n * });\r\n * ```\r\n */\r\nexport const createResponsiveAttributes = (\r\n config: UrlBuilderConfig,\r\n options: ResponsiveImageOptions = {}\r\n): ResponsiveImageResult => {\r\n const {\r\n sizes,\r\n width,\r\n deviceBreakpoints = [...DEFAULT_DEVICE_BREAKPOINTS],\r\n imageBreakpoints = [...DEFAULT_IMAGE_BREAKPOINTS],\r\n transformations = {},\r\n mode = 'short',\r\n } = options;\r\n\r\n const sortedDeviceBreakpoints = [...deviceBreakpoints].sort((a, b) => a - b);\r\n const sortedImageBreakpoints = [...imageBreakpoints].sort((a, b) => a - b);\r\n\r\n const allBreakpoints = [...new Set([...sortedImageBreakpoints, ...sortedDeviceBreakpoints])].sort((a, b) => a - b);\r\n\r\n const { candidates, descriptor } = computeCandidateWidths({\r\n allBreakpoints,\r\n deviceBreakpoints: sortedDeviceBreakpoints,\r\n ...(width !== undefined && { explicitWidth: width }),\r\n ...(sizes !== undefined && { sizesAttr: sizes }),\r\n });\r\n\r\n if (candidates.length === 0) {\r\n // Fallback: no valid candidates, return base URL\r\n return { src: config.baseUrl };\r\n }\r\n\r\n // Build srcSet entries\r\n const srcSetEntries = candidates.map((w, index,) => buildSrcEntries(w, index, config, transformations, mode, descriptor));\r\n\r\n // Default src is the largest candidate\r\n const largestWidth = candidates[candidates.length - 1]!;\r\n const src = buildTransformationUrl(\r\n config,\r\n { ...transformations, width: largestWidth },\r\n mode\r\n );\r\n\r\n const endSizes = sizes ?? (descriptor === 'w' ? '100vw' : undefined);\r\n\r\n return {\r\n src,\r\n ...(endSizes ? { sizes: endSizes } : {}),\r\n srcSet: srcSetEntries.join(', '),\r\n ...(width !== undefined ? { width } : {}),\r\n };\r\n};\r\n\r\nfunction buildSrcEntries(width: number, index: number, config: UrlBuilderConfig, transformations: TransformationOptions, mode: NameMode, descriptor: 'w' | 'x'): string {\r\n const url = buildTransformationUrl(\r\n config,\r\n { ...transformations, width: width },\r\n mode\r\n );\r\n\r\n const desc = descriptor === 'w' ? `${width}w` : `${index + 1}x`;\r\n\r\n return `${url} ${desc}`;\r\n}\r\n","import { Path, PathValue } from './util-types';\r\nimport { MAX_IMAGE_WIDTH } from './src-set';\r\n\r\nexport const normalizeHex = (value: string) => value.replace('#', '')\r\n\r\n\r\nexport const removeTrailingSlash = (baseUrl: string) => {\r\n return baseUrl.endsWith('/')\r\n ? baseUrl.slice(0, -1)\r\n : baseUrl;\r\n}\r\n\r\nexport const getNestedValue = <\r\n T,\r\n P extends Path<T>\r\n>(\r\n obj: T,\r\n path: P\r\n): PathValue<T, P> | undefined =>\r\n path.reduce<any>(\r\n (acc, key) => (acc == null ? undefined : acc[key]),\r\n obj\r\n );\r\n\r\n\r\nexport const clampMax = (v: number) => Math.min(v, MAX_IMAGE_WIDTH);\r\n\r\nexport const nearest = (sortedBreakpoints: number[],target: number) =>\r\n sortedBreakpoints.find(w => w >= target) ?? sortedBreakpoints[sortedBreakpoints.length - 1]!;\r\n\r\nexport const parseVW = (sizes?: string): number[] => {\r\n if (!sizes) return [];\r\n return [...sizes.matchAll(/(\\d+(?:\\.\\d+)?)vw/g)].map(\r\n m => Number(m[1]) / 100\r\n );\r\n};\r\n\r\n","import { ParamSpec, Path } from './util-types';\r\nimport { TransformationOptions } from './types';\r\nimport { normalizeHex } from './utils';\r\n\r\nexport const PARAMS = [\r\n {path: ['width'], long: 'width', short: 'w'},\r\n {path: ['height'], long: 'height', short: 'h'},\r\n {path: ['dpr'], long: 'dpr', short: 'dpr'},\r\n {path: ['mode'], long: 'mode', short: 'rm'},\r\n {\r\n path: ['background'],\r\n long: 'background',\r\n short: 'bg',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n\r\n {path: ['format'], long: 'format', short: 'f'},\r\n {path: ['quality'], long: 'quality', short: 'q'},\r\n {path: ['stripMetadata'], long: 'stripMetadata', short: 'st'},\r\n\r\n {path: ['blur'], long: 'blur', short: 'bl'},\r\n {path: ['brightness'], long: 'brightness', short: 'br'},\r\n {path: ['contrast'], long: 'contrast', short: 'ct'},\r\n {path: ['grayscale'], long: 'grayscale', short: 'gs'},\r\n {path: ['saturation'], long: 'saturation', short: 'sa'},\r\n {path: ['sepia'], long: 'sepia', short: 'sp'},\r\n {path: ['sharpen'], long: 'sharpen', short: 'sh'},\r\n {path: ['noise'], long: 'noise', short: 'ns'},\r\n {path: ['rotate'], long: 'rotate', short: 'rt'},\r\n\r\n {path: ['border', 'width'], long: 'border.width', short: 'bo.w'},\r\n {\r\n path: ['border', 'color'],\r\n long: 'border.color',\r\n short: 'bo.c',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['border', 'radius'], long: 'border.radius', short: 'bo.r'},\r\n\r\n {path: ['crop', 'type'], long: 'crop.type', short: 'cr.t'},\r\n {path: ['crop', 'objectType'], long: 'crop.objectType', short: 'cr.o'},\r\n {path: ['crop', 'focus', 'position', 'x'], long: 'crop.x', short: 'cr.x'},\r\n {path: ['crop', 'focus', 'position', 'y'], long: 'crop.y', short: 'cr.y'},\r\n {path: ['crop', 'focus', 'strategy'], long: 'crop', short: 'cr'},\r\n {path: ['crop', 'width'], long: 'crop.width', short: 'cr.w'},\r\n {path: ['crop', 'height'], long: 'crop.height', short: 'cr.h'},\r\n {path: ['crop', 'afterResize'], long: 'crop.afterResize', short: 'cr.ar'},\r\n\r\n {path: ['text', 'text'], long: 'text', short: 'tx'},\r\n {\r\n path: ['text', 'color'],\r\n long: 'text.color',\r\n short: 'tx.c',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['text', 'opacity'], long: 'text.opacity', short: 'tx.o'},\r\n {path: ['text', 'size'], long: 'text.size', short: 'tx.s'},\r\n {path: ['text', 'font'], long: 'text.font', short: 'tx.f'},\r\n {path: ['text', 'fontSize'], long: 'text.fontSize', short: 'tx.fs'},\r\n {path: ['text', 'position'], long: 'text.position', short: 'tx.p'},\r\n {\r\n path: ['text', 'background'],\r\n long: 'text.background',\r\n short: 'tx.bg',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['text', 'backgroundOpacity'], long: 'text.backgroundOpacity', short: 'tx.bg.o'},\r\n\r\n {path: ['watermark', 'name'], long: 'watermark', short: 'wm'},\r\n {path: ['watermark', 'opacity'], long: 'watermark.opacity', short: 'wm.o'},\r\n {path: ['watermark', 'size'], long: 'watermark.size', short: 'wm.s'},\r\n {path: ['watermark', 'width'], long: 'watermark.width', short: 'wm.w'},\r\n {path: ['watermark', 'height'], long: 'watermark.height', short: 'wm.h'},\r\n {path: ['watermark', 'position'], long: 'watermark.position', short: 'wm.p'},\r\n] as const satisfies readonly ParamSpec<\r\n TransformationOptions,\r\n Path<TransformationOptions>\r\n>[];\r\n","import { TransformationOptions, UrlBuilderConfig } from './types';\r\nimport { getNestedValue, removeTrailingSlash } from './utils';\r\nimport { PARAMS } from './config';\r\n\r\n/**\r\n * Determines which parameter names to use in the query string.\r\n * - `'short'` - Use abbreviated aliases (e.g., `w`, `h`, `q`)\r\n * - `'long'` - Use full parameter names (e.g., `width`, `height`, `quality`)\r\n */\r\nexport type NameMode = 'short' | 'long';\r\n\r\n/**\r\n * Converts transformation options into a URL-encoded query string.\r\n *\r\n * Iterates through all defined parameters in the PARAMS config, extracts values\r\n * from the options object using their paths, applies any normalization functions\r\n * (e.g., stripping `#` from hex colors), and returns a URL-encoded query string.\r\n *\r\n * @param options - The transformation options object\r\n * @param mode - Whether to use short aliases or long parameter names (default: `'short'`)\r\n * @returns URL-encoded query string (without leading `?`)\r\n *\r\n * @example\r\n * ```ts\r\n * // Using short aliases (default)\r\n * buildQueryParams({ width: 800, height: 600, quality: 80 });\r\n * // => 'w=800&h=600&q=80'\r\n *\r\n * // Using long parameter names\r\n * buildQueryParams({ width: 800, format: 'webp' }, 'long');\r\n * // => 'width=800&format=webp'\r\n *\r\n * // Nested options\r\n * buildQueryParams({ border: { width: 2, color: '#ff0000' } });\r\n * // => 'bo.w=2&bo.c=ff0000'\r\n * ```\r\n */\r\nexport const buildTransformationQueryParams = (\r\n options: TransformationOptions,\r\n mode: NameMode = 'short'\r\n): string => {\r\n const params: Array<[string, string]> = [];\r\n\r\n for (const spec of PARAMS) {\r\n const value = getNestedValue(options, spec.path);\r\n if (value == null) continue;\r\n\r\n const name =\r\n mode === 'short' && spec.short ? spec.short : spec.long;\r\n\r\n let normalized = value\r\n if ('normalize' in spec) {\r\n normalized = spec.normalize(value)\r\n }\r\n\r\n params.push([name, String(normalized)]);\r\n }\r\n\r\n\r\n return new URLSearchParams(\r\n params.map(([k, v]) => [k, v])\r\n ).toString();\r\n\r\n};\r\n\r\n/**\r\n * Builds a complete transformation URL from a base URL and transformation options.\r\n *\r\n * Combines the configured base URL with query parameters generated from the\r\n * transformation options. Handles the special `original` flag which returns\r\n * the unmodified source image.\r\n *\r\n * @param config - Configuration containing the base URL for the image\r\n * @param options - The transformation options to apply\r\n * @param mode - Whether to use short aliases or long parameter names (default: `'short'`)\r\n * @returns The complete URL string with query parameters\r\n *\r\n * @example\r\n * ```ts\r\n * const config = { baseUrl: 'https://example.com/images/photo.jpg' };\r\n *\r\n * // Basic resize\r\n * buildTransformationUrl(config, { width: 400, height: 300 });\r\n * // => 'https://example.com/images/photo.jpg?w=400&h=300'\r\n *\r\n * // Multiple transformations\r\n * buildTransformationUrl(config, {\r\n * width: 800,\r\n * format: 'webp',\r\n * quality: 85,\r\n * blur: 5\r\n * });\r\n * // => 'https://example.com/images/photo.jpg?w=800&f=webp&q=85&bl=5'\r\n *\r\n * // Request original image (no transformations)\r\n * buildTransformationUrl(config, { original: true });\r\n * // => 'https://example.com/images/photo.jpg?original'\r\n *\r\n * // Using long parameter names\r\n * buildTransformationUrl(config, { width: 400 }, 'long');\r\n * // => 'https://example.com/images/photo.jpg?width=400'\r\n * ```\r\n */\r\nexport const buildTransformationUrl = (\r\n config: UrlBuilderConfig,\r\n options: TransformationOptions,\r\n mode: NameMode = 'short'\r\n) => {\r\n if (options.original) {\r\n return `${config.baseUrl}?original`;\r\n }\r\n\r\n const query = buildTransformationQueryParams(options, mode);\r\n\r\n const baseUrl = removeTrailingSlash(config.baseUrl)\r\n return `${baseUrl}?${query}`;\r\n};\r\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -9,7 +9,7 @@ type ResizeMode = 'FILL' | 'FIT' | 'PAD' | 'FORCE' | 'COVER';
|
|
|
9
9
|
/**
|
|
10
10
|
* Output image format - null or empty means AUTO
|
|
11
11
|
*/
|
|
12
|
-
type ImageFormat = '
|
|
12
|
+
type ImageFormat = 'JPG' | 'PNG' | 'WEBP' | 'AVIF' | 'GIF';
|
|
13
13
|
/**
|
|
14
14
|
* Position for text overlays and watermarks
|
|
15
15
|
*/
|
|
@@ -321,4 +321,82 @@ declare const buildTransformationQueryParams: (options: TransformationOptions, m
|
|
|
321
321
|
*/
|
|
322
322
|
declare const buildTransformationUrl: (config: UrlBuilderConfig, options: TransformationOptions, mode?: NameMode) => string;
|
|
323
323
|
|
|
324
|
-
|
|
324
|
+
/**
|
|
325
|
+
* Options for responsive image attributes generation
|
|
326
|
+
*/
|
|
327
|
+
interface ResponsiveImageOptions {
|
|
328
|
+
/**
|
|
329
|
+
* The `sizes` attribute value (e.g., "100vw", "(max-width: 768px) 100vw, 50vw").
|
|
330
|
+
* When provided, generates width descriptors (`w`) for responsive layouts.
|
|
331
|
+
*/
|
|
332
|
+
sizes?: string;
|
|
333
|
+
/**
|
|
334
|
+
* Fixed display width in CSS pixels.
|
|
335
|
+
* When provided without `sizes`, generates DPR descriptors (`x`).
|
|
336
|
+
*/
|
|
337
|
+
width?: number;
|
|
338
|
+
/** Device breakpoints for viewport-based sizing (default: DEFAULT_DEVICE_BREAKPOINTS) */
|
|
339
|
+
deviceBreakpoints?: number[];
|
|
340
|
+
/** Image breakpoints for smaller images (default: DEFAULT_IMAGE_BREAKPOINTS) */
|
|
341
|
+
imageBreakpoints?: number[];
|
|
342
|
+
/** Base transformation options to apply to all variants */
|
|
343
|
+
transformations?: TransformationOptions;
|
|
344
|
+
/** Whether to use short or long parameter names (default: 'short') */
|
|
345
|
+
mode?: NameMode;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Result of responsive image attributes generation
|
|
349
|
+
*/
|
|
350
|
+
interface ResponsiveImageResult {
|
|
351
|
+
/** The src attribute value (largest/default variant) */
|
|
352
|
+
src: string;
|
|
353
|
+
/** The srcset attribute value */
|
|
354
|
+
srcSet?: string;
|
|
355
|
+
/** The sizes attribute value (for width descriptors) */
|
|
356
|
+
sizes?: string;
|
|
357
|
+
/** The width attribute value (when explicitly provided) */
|
|
358
|
+
width?: number;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Generates responsive image attributes (src, srcSet, sizes) for optimal loading.
|
|
362
|
+
*
|
|
363
|
+
* Supports two strategies:
|
|
364
|
+
* 1. **Responsive (with `sizes`)**: Uses width descriptors (`w`) for fluid layouts
|
|
365
|
+
* where image size varies with viewport.
|
|
366
|
+
* 2. **Fixed width (with `width`)**: Uses DPR descriptors (`x`) for fixed-size
|
|
367
|
+
* images that need high-DPI support.
|
|
368
|
+
*
|
|
369
|
+
* @param config - Configuration containing the base URL for the image
|
|
370
|
+
* @param options - Options for responsive image generation
|
|
371
|
+
* @returns Object containing src, srcSet, and optionally sizes attributes
|
|
372
|
+
*
|
|
373
|
+
* @example
|
|
374
|
+
* ```ts
|
|
375
|
+
* const config = { baseUrl: 'https://example.com/photo.jpg' };
|
|
376
|
+
*
|
|
377
|
+
* // Responsive image with sizes attribute (uses `w` descriptors)
|
|
378
|
+
* createResponsiveAttributes(config, { sizes: '(max-width: 768px) 100vw, 50vw' });
|
|
379
|
+
* // => {
|
|
380
|
+
* // src: '...?w=3840',
|
|
381
|
+
* // srcSet: '...?w=640 640w, ...?w=750 750w, ..., ...?w=3840 3840w',
|
|
382
|
+
* // sizes: '(max-width: 768px) 100vw, 50vw'
|
|
383
|
+
* // }
|
|
384
|
+
*
|
|
385
|
+
* // Fixed-width image (uses `x` descriptors)
|
|
386
|
+
* createResponsiveAttributes(config, { width: 400 });
|
|
387
|
+
* // => {
|
|
388
|
+
* // src: '...?w=384',
|
|
389
|
+
* // srcSet: '...?w=384 1x, ...?w=828 2x',
|
|
390
|
+
* // width: 400
|
|
391
|
+
* // }
|
|
392
|
+
*
|
|
393
|
+
* // With transformations
|
|
394
|
+
* createResponsiveAttributes(config, {
|
|
395
|
+
* sizes: '100vw',
|
|
396
|
+
* transformations: { format: 'WEBP', quality: 80 }
|
|
397
|
+
* });
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
400
|
+
declare const createResponsiveAttributes: (config: UrlBuilderConfig, options?: ResponsiveImageOptions) => ResponsiveImageResult;
|
|
401
|
+
|
|
402
|
+
export { type AutoCropFocus, type BorderOptions, type CropFocus, type CropFocusOptions, type CropFocusPosition, type CropFocusStrategy, type CropObjectType, type CropOptions, type CropType, type DevicePixelRatio, type EffectOptions, type FormatOptions, type HexColor, type ImageFormat, type ObjectCropOptions, type PixelFiddlerConfig, type Position, type ResizeMode, type ResizeOptions, type RotationAngle, type SimpleCropOptions, type SpecifiedCropFocus, type TextOptions, type TransformationOptions, type UrlBuilderConfig, type WatermarkOptions, buildTransformationQueryParams, buildTransformationUrl, createResponsiveAttributes };
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ type ResizeMode = 'FILL' | 'FIT' | 'PAD' | 'FORCE' | 'COVER';
|
|
|
9
9
|
/**
|
|
10
10
|
* Output image format - null or empty means AUTO
|
|
11
11
|
*/
|
|
12
|
-
type ImageFormat = '
|
|
12
|
+
type ImageFormat = 'JPG' | 'PNG' | 'WEBP' | 'AVIF' | 'GIF';
|
|
13
13
|
/**
|
|
14
14
|
* Position for text overlays and watermarks
|
|
15
15
|
*/
|
|
@@ -321,4 +321,82 @@ declare const buildTransformationQueryParams: (options: TransformationOptions, m
|
|
|
321
321
|
*/
|
|
322
322
|
declare const buildTransformationUrl: (config: UrlBuilderConfig, options: TransformationOptions, mode?: NameMode) => string;
|
|
323
323
|
|
|
324
|
-
|
|
324
|
+
/**
|
|
325
|
+
* Options for responsive image attributes generation
|
|
326
|
+
*/
|
|
327
|
+
interface ResponsiveImageOptions {
|
|
328
|
+
/**
|
|
329
|
+
* The `sizes` attribute value (e.g., "100vw", "(max-width: 768px) 100vw, 50vw").
|
|
330
|
+
* When provided, generates width descriptors (`w`) for responsive layouts.
|
|
331
|
+
*/
|
|
332
|
+
sizes?: string;
|
|
333
|
+
/**
|
|
334
|
+
* Fixed display width in CSS pixels.
|
|
335
|
+
* When provided without `sizes`, generates DPR descriptors (`x`).
|
|
336
|
+
*/
|
|
337
|
+
width?: number;
|
|
338
|
+
/** Device breakpoints for viewport-based sizing (default: DEFAULT_DEVICE_BREAKPOINTS) */
|
|
339
|
+
deviceBreakpoints?: number[];
|
|
340
|
+
/** Image breakpoints for smaller images (default: DEFAULT_IMAGE_BREAKPOINTS) */
|
|
341
|
+
imageBreakpoints?: number[];
|
|
342
|
+
/** Base transformation options to apply to all variants */
|
|
343
|
+
transformations?: TransformationOptions;
|
|
344
|
+
/** Whether to use short or long parameter names (default: 'short') */
|
|
345
|
+
mode?: NameMode;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Result of responsive image attributes generation
|
|
349
|
+
*/
|
|
350
|
+
interface ResponsiveImageResult {
|
|
351
|
+
/** The src attribute value (largest/default variant) */
|
|
352
|
+
src: string;
|
|
353
|
+
/** The srcset attribute value */
|
|
354
|
+
srcSet?: string;
|
|
355
|
+
/** The sizes attribute value (for width descriptors) */
|
|
356
|
+
sizes?: string;
|
|
357
|
+
/** The width attribute value (when explicitly provided) */
|
|
358
|
+
width?: number;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Generates responsive image attributes (src, srcSet, sizes) for optimal loading.
|
|
362
|
+
*
|
|
363
|
+
* Supports two strategies:
|
|
364
|
+
* 1. **Responsive (with `sizes`)**: Uses width descriptors (`w`) for fluid layouts
|
|
365
|
+
* where image size varies with viewport.
|
|
366
|
+
* 2. **Fixed width (with `width`)**: Uses DPR descriptors (`x`) for fixed-size
|
|
367
|
+
* images that need high-DPI support.
|
|
368
|
+
*
|
|
369
|
+
* @param config - Configuration containing the base URL for the image
|
|
370
|
+
* @param options - Options for responsive image generation
|
|
371
|
+
* @returns Object containing src, srcSet, and optionally sizes attributes
|
|
372
|
+
*
|
|
373
|
+
* @example
|
|
374
|
+
* ```ts
|
|
375
|
+
* const config = { baseUrl: 'https://example.com/photo.jpg' };
|
|
376
|
+
*
|
|
377
|
+
* // Responsive image with sizes attribute (uses `w` descriptors)
|
|
378
|
+
* createResponsiveAttributes(config, { sizes: '(max-width: 768px) 100vw, 50vw' });
|
|
379
|
+
* // => {
|
|
380
|
+
* // src: '...?w=3840',
|
|
381
|
+
* // srcSet: '...?w=640 640w, ...?w=750 750w, ..., ...?w=3840 3840w',
|
|
382
|
+
* // sizes: '(max-width: 768px) 100vw, 50vw'
|
|
383
|
+
* // }
|
|
384
|
+
*
|
|
385
|
+
* // Fixed-width image (uses `x` descriptors)
|
|
386
|
+
* createResponsiveAttributes(config, { width: 400 });
|
|
387
|
+
* // => {
|
|
388
|
+
* // src: '...?w=384',
|
|
389
|
+
* // srcSet: '...?w=384 1x, ...?w=828 2x',
|
|
390
|
+
* // width: 400
|
|
391
|
+
* // }
|
|
392
|
+
*
|
|
393
|
+
* // With transformations
|
|
394
|
+
* createResponsiveAttributes(config, {
|
|
395
|
+
* sizes: '100vw',
|
|
396
|
+
* transformations: { format: 'WEBP', quality: 80 }
|
|
397
|
+
* });
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
400
|
+
declare const createResponsiveAttributes: (config: UrlBuilderConfig, options?: ResponsiveImageOptions) => ResponsiveImageResult;
|
|
401
|
+
|
|
402
|
+
export { type AutoCropFocus, type BorderOptions, type CropFocus, type CropFocusOptions, type CropFocusPosition, type CropFocusStrategy, type CropObjectType, type CropOptions, type CropType, type DevicePixelRatio, type EffectOptions, type FormatOptions, type HexColor, type ImageFormat, type ObjectCropOptions, type PixelFiddlerConfig, type Position, type ResizeMode, type ResizeOptions, type RotationAngle, type SimpleCropOptions, type SpecifiedCropFocus, type TextOptions, type TransformationOptions, type UrlBuilderConfig, type WatermarkOptions, buildTransformationQueryParams, buildTransformationUrl, createResponsiveAttributes };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,110 @@
|
|
|
1
|
+
// src/src-set.ts
|
|
2
|
+
var MAX_IMAGE_WIDTH = 4096;
|
|
3
|
+
var DEFAULT_DEVICE_BREAKPOINTS = [
|
|
4
|
+
320,
|
|
5
|
+
375,
|
|
6
|
+
640,
|
|
7
|
+
768,
|
|
8
|
+
1024,
|
|
9
|
+
1280,
|
|
10
|
+
1536,
|
|
11
|
+
1920
|
|
12
|
+
];
|
|
13
|
+
var DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 384];
|
|
14
|
+
var DEFAULT_MAX_DPR = 2;
|
|
15
|
+
function computeCandidateWidths(params) {
|
|
16
|
+
const {
|
|
17
|
+
allBreakpoints,
|
|
18
|
+
deviceBreakpoints,
|
|
19
|
+
explicitWidth,
|
|
20
|
+
sizesAttr
|
|
21
|
+
} = params;
|
|
22
|
+
const sortedAll = [...new Set(allBreakpoints)].sort((a, b) => a - b);
|
|
23
|
+
const sortedDevice = [...new Set(deviceBreakpoints)].sort((a, b) => a - b);
|
|
24
|
+
if (!sortedAll.length || !sortedDevice.length) {
|
|
25
|
+
return { candidates: [], descriptor: "w" };
|
|
26
|
+
}
|
|
27
|
+
const minDevice = sortedDevice[0];
|
|
28
|
+
const maxDevice = sortedDevice[sortedDevice.length - 1];
|
|
29
|
+
const vwRatios = parseVW(sizesAttr);
|
|
30
|
+
const hasVW = vwRatios.length > 0;
|
|
31
|
+
if (typeof explicitWidth === "number" && !hasVW) {
|
|
32
|
+
const DPRs = [1, 2, 3];
|
|
33
|
+
const candidates = Array.from(
|
|
34
|
+
new Set(
|
|
35
|
+
DPRs.map((dpr) => nearest(sortedAll, explicitWidth * dpr)).map(clampMax)
|
|
36
|
+
)
|
|
37
|
+
).filter((w) => w <= MAX_IMAGE_WIDTH);
|
|
38
|
+
return {
|
|
39
|
+
candidates,
|
|
40
|
+
descriptor: "x"
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (hasVW) {
|
|
44
|
+
const minRatio = Math.min(...vwRatios);
|
|
45
|
+
const maxRatio = Math.max(...vwRatios);
|
|
46
|
+
const minPx = minDevice * minRatio;
|
|
47
|
+
const maxPx = maxDevice * maxRatio * DEFAULT_MAX_DPR;
|
|
48
|
+
const candidates = sortedAll.filter(
|
|
49
|
+
(w) => w >= minPx && w <= clampMax(maxPx)
|
|
50
|
+
);
|
|
51
|
+
return {
|
|
52
|
+
candidates,
|
|
53
|
+
descriptor: "w"
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const maxUseful = maxDevice * DEFAULT_MAX_DPR;
|
|
57
|
+
return {
|
|
58
|
+
candidates: sortedAll.filter((w) => w <= clampMax(maxUseful)),
|
|
59
|
+
descriptor: "w"
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
var createResponsiveAttributes = (config, options = {}) => {
|
|
63
|
+
const {
|
|
64
|
+
sizes,
|
|
65
|
+
width,
|
|
66
|
+
deviceBreakpoints = [...DEFAULT_DEVICE_BREAKPOINTS],
|
|
67
|
+
imageBreakpoints = [...DEFAULT_IMAGE_BREAKPOINTS],
|
|
68
|
+
transformations = {},
|
|
69
|
+
mode = "short"
|
|
70
|
+
} = options;
|
|
71
|
+
const sortedDeviceBreakpoints = [...deviceBreakpoints].sort((a, b) => a - b);
|
|
72
|
+
const sortedImageBreakpoints = [...imageBreakpoints].sort((a, b) => a - b);
|
|
73
|
+
const allBreakpoints = [.../* @__PURE__ */ new Set([...sortedImageBreakpoints, ...sortedDeviceBreakpoints])].sort((a, b) => a - b);
|
|
74
|
+
const { candidates, descriptor } = computeCandidateWidths({
|
|
75
|
+
allBreakpoints,
|
|
76
|
+
deviceBreakpoints: sortedDeviceBreakpoints,
|
|
77
|
+
...width !== void 0 && { explicitWidth: width },
|
|
78
|
+
...sizes !== void 0 && { sizesAttr: sizes }
|
|
79
|
+
});
|
|
80
|
+
if (candidates.length === 0) {
|
|
81
|
+
return { src: config.baseUrl };
|
|
82
|
+
}
|
|
83
|
+
const srcSetEntries = candidates.map((w, index) => buildSrcEntries(w, index, config, transformations, mode, descriptor));
|
|
84
|
+
const largestWidth = candidates[candidates.length - 1];
|
|
85
|
+
const src = buildTransformationUrl(
|
|
86
|
+
config,
|
|
87
|
+
{ ...transformations, width: largestWidth },
|
|
88
|
+
mode
|
|
89
|
+
);
|
|
90
|
+
const endSizes = sizes ?? (descriptor === "w" ? "100vw" : void 0);
|
|
91
|
+
return {
|
|
92
|
+
src,
|
|
93
|
+
...endSizes ? { sizes: endSizes } : {},
|
|
94
|
+
srcSet: srcSetEntries.join(", "),
|
|
95
|
+
...width !== void 0 ? { width } : {}
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
function buildSrcEntries(width, index, config, transformations, mode, descriptor) {
|
|
99
|
+
const url = buildTransformationUrl(
|
|
100
|
+
config,
|
|
101
|
+
{ ...transformations, width },
|
|
102
|
+
mode
|
|
103
|
+
);
|
|
104
|
+
const desc = descriptor === "w" ? `${width}w` : `${index + 1}x`;
|
|
105
|
+
return `${url} ${desc}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
1
108
|
// src/utils.ts
|
|
2
109
|
var normalizeHex = (value) => value.replace("#", "");
|
|
3
110
|
var removeTrailingSlash = (baseUrl) => {
|
|
@@ -7,6 +114,14 @@ var getNestedValue = (obj, path) => path.reduce(
|
|
|
7
114
|
(acc, key) => acc == null ? void 0 : acc[key],
|
|
8
115
|
obj
|
|
9
116
|
);
|
|
117
|
+
var clampMax = (v) => Math.min(v, MAX_IMAGE_WIDTH);
|
|
118
|
+
var nearest = (sortedBreakpoints, target) => sortedBreakpoints.find((w) => w >= target) ?? sortedBreakpoints[sortedBreakpoints.length - 1];
|
|
119
|
+
var parseVW = (sizes) => {
|
|
120
|
+
if (!sizes) return [];
|
|
121
|
+
return [...sizes.matchAll(/(\d+(?:\.\d+)?)vw/g)].map(
|
|
122
|
+
(m) => Number(m[1]) / 100
|
|
123
|
+
);
|
|
124
|
+
};
|
|
10
125
|
|
|
11
126
|
// src/config.ts
|
|
12
127
|
var PARAMS = [
|
|
@@ -101,6 +216,6 @@ var buildTransformationUrl = (config, options, mode = "short") => {
|
|
|
101
216
|
return `${baseUrl}?${query}`;
|
|
102
217
|
};
|
|
103
218
|
|
|
104
|
-
export { buildTransformationQueryParams, buildTransformationUrl };
|
|
219
|
+
export { buildTransformationQueryParams, buildTransformationUrl, createResponsiveAttributes };
|
|
105
220
|
//# sourceMappingURL=index.js.map
|
|
106
221
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils.ts","../src/config.ts","../src/url-builder.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { Path, PathValue } from './util-types';\r\n\r\nexport const normalizeHex = (value: string) => value.replace('#', '')\r\n\r\n\r\nexport const removeTrailingSlash = (baseUrl: string) => {\r\n return baseUrl.endsWith('/')\r\n ? baseUrl.slice(0, -1)\r\n : baseUrl;\r\n}\r\n\r\nexport const getNestedValue = <\r\n T,\r\n P extends Path<T>\r\n>(\r\n obj: T,\r\n path: P\r\n): PathValue<T, P> | undefined =>\r\n path.reduce<any>(\r\n (acc, key) => (acc == null ? undefined : acc[key]),\r\n obj\r\n );\r\n\r\n","import { ParamSpec, Path } from './util-types';\r\nimport { TransformationOptions } from './types';\r\nimport { normalizeHex } from './utils';\r\n\r\nexport const PARAMS = [\r\n {path: ['width'], long: 'width', short: 'w'},\r\n {path: ['height'], long: 'height', short: 'h'},\r\n {path: ['dpr'], long: 'dpr', short: 'dpr'},\r\n {path: ['mode'], long: 'mode', short: 'rm'},\r\n {\r\n path: ['background'],\r\n long: 'background',\r\n short: 'bg',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n\r\n {path: ['format'], long: 'format', short: 'f'},\r\n {path: ['quality'], long: 'quality', short: 'q'},\r\n {path: ['stripMetadata'], long: 'stripMetadata', short: 'st'},\r\n\r\n {path: ['blur'], long: 'blur', short: 'bl'},\r\n {path: ['brightness'], long: 'brightness', short: 'br'},\r\n {path: ['contrast'], long: 'contrast', short: 'ct'},\r\n {path: ['grayscale'], long: 'grayscale', short: 'gs'},\r\n {path: ['saturation'], long: 'saturation', short: 'sa'},\r\n {path: ['sepia'], long: 'sepia', short: 'sp'},\r\n {path: ['sharpen'], long: 'sharpen', short: 'sh'},\r\n {path: ['noise'], long: 'noise', short: 'ns'},\r\n {path: ['rotate'], long: 'rotate', short: 'rt'},\r\n\r\n {path: ['border', 'width'], long: 'border.width', short: 'bo.w'},\r\n {\r\n path: ['border', 'color'],\r\n long: 'border.color',\r\n short: 'bo.c',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['border', 'radius'], long: 'border.radius', short: 'bo.r'},\r\n\r\n {path: ['crop', 'type'], long: 'crop.type', short: 'cr.t'},\r\n {path: ['crop', 'objectType'], long: 'crop.objectType', short: 'cr.o'},\r\n {path: ['crop', 'focus', 'position', 'x'], long: 'crop.x', short: 'cr.x'},\r\n {path: ['crop', 'focus', 'position', 'y'], long: 'crop.y', short: 'cr.y'},\r\n {path: ['crop', 'focus', 'strategy'], long: 'crop', short: 'cr'},\r\n {path: ['crop', 'width'], long: 'crop.width', short: 'cr.w'},\r\n {path: ['crop', 'height'], long: 'crop.height', short: 'cr.h'},\r\n {path: ['crop', 'afterResize'], long: 'crop.afterResize', short: 'cr.ar'},\r\n\r\n {path: ['text', 'text'], long: 'text', short: 'tx'},\r\n {\r\n path: ['text', 'color'],\r\n long: 'text.color',\r\n short: 'tx.c',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['text', 'opacity'], long: 'text.opacity', short: 'tx.o'},\r\n {path: ['text', 'size'], long: 'text.size', short: 'tx.s'},\r\n {path: ['text', 'font'], long: 'text.font', short: 'tx.f'},\r\n {path: ['text', 'fontSize'], long: 'text.fontSize', short: 'tx.fs'},\r\n {path: ['text', 'position'], long: 'text.position', short: 'tx.p'},\r\n {\r\n path: ['text', 'background'],\r\n long: 'text.background',\r\n short: 'tx.bg',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['text', 'backgroundOpacity'], long: 'text.backgroundOpacity', short: 'tx.bg.o'},\r\n\r\n {path: ['watermark', 'name'], long: 'watermark', short: 'wm'},\r\n {path: ['watermark', 'opacity'], long: 'watermark.opacity', short: 'wm.o'},\r\n {path: ['watermark', 'size'], long: 'watermark.size', short: 'wm.s'},\r\n {path: ['watermark', 'width'], long: 'watermark.width', short: 'wm.w'},\r\n {path: ['watermark', 'height'], long: 'watermark.height', short: 'wm.h'},\r\n {path: ['watermark', 'position'], long: 'watermark.position', short: 'wm.p'},\r\n] as const satisfies readonly ParamSpec<\r\n TransformationOptions,\r\n Path<TransformationOptions>\r\n>[];\r\n","import { TransformationOptions, UrlBuilderConfig } from './types';\r\nimport { getNestedValue, removeTrailingSlash } from './utils';\r\nimport { PARAMS } from './config';\r\n\r\n/**\r\n * Determines which parameter names to use in the query string.\r\n * - `'short'` - Use abbreviated aliases (e.g., `w`, `h`, `q`)\r\n * - `'long'` - Use full parameter names (e.g., `width`, `height`, `quality`)\r\n */\r\ntype NameMode = 'short' | 'long';\r\n\r\n/**\r\n * Converts transformation options into a URL-encoded query string.\r\n *\r\n * Iterates through all defined parameters in the PARAMS config, extracts values\r\n * from the options object using their paths, applies any normalization functions\r\n * (e.g., stripping `#` from hex colors), and returns a URL-encoded query string.\r\n *\r\n * @param options - The transformation options object\r\n * @param mode - Whether to use short aliases or long parameter names (default: `'short'`)\r\n * @returns URL-encoded query string (without leading `?`)\r\n *\r\n * @example\r\n * ```ts\r\n * // Using short aliases (default)\r\n * buildQueryParams({ width: 800, height: 600, quality: 80 });\r\n * // => 'w=800&h=600&q=80'\r\n *\r\n * // Using long parameter names\r\n * buildQueryParams({ width: 800, format: 'webp' }, 'long');\r\n * // => 'width=800&format=webp'\r\n *\r\n * // Nested options\r\n * buildQueryParams({ border: { width: 2, color: '#ff0000' } });\r\n * // => 'bo.w=2&bo.c=ff0000'\r\n * ```\r\n */\r\nexport const buildTransformationQueryParams = (\r\n options: TransformationOptions,\r\n mode: NameMode = 'short'\r\n): string => {\r\n const params: Array<[string, string]> = [];\r\n\r\n for (const spec of PARAMS) {\r\n const value = getNestedValue(options, spec.path);\r\n if (value == null) continue;\r\n\r\n const name =\r\n mode === 'short' && spec.short ? spec.short : spec.long;\r\n\r\n let normalized = value\r\n if ('normalize' in spec) {\r\n normalized = spec.normalize(value)\r\n }\r\n\r\n params.push([name, String(normalized)]);\r\n }\r\n\r\n\r\n return new URLSearchParams(\r\n params.map(([k, v]) => [k, v])\r\n ).toString();\r\n\r\n};\r\n\r\n/**\r\n * Builds a complete transformation URL from a base URL and transformation options.\r\n *\r\n * Combines the configured base URL with query parameters generated from the\r\n * transformation options. Handles the special `original` flag which returns\r\n * the unmodified source image.\r\n *\r\n * @param config - Configuration containing the base URL for the image\r\n * @param options - The transformation options to apply\r\n * @param mode - Whether to use short aliases or long parameter names (default: `'short'`)\r\n * @returns The complete URL string with query parameters\r\n *\r\n * @example\r\n * ```ts\r\n * const config = { baseUrl: 'https://example.com/images/photo.jpg' };\r\n *\r\n * // Basic resize\r\n * buildTransformationUrl(config, { width: 400, height: 300 });\r\n * // => 'https://example.com/images/photo.jpg?w=400&h=300'\r\n *\r\n * // Multiple transformations\r\n * buildTransformationUrl(config, {\r\n * width: 800,\r\n * format: 'webp',\r\n * quality: 85,\r\n * blur: 5\r\n * });\r\n * // => 'https://example.com/images/photo.jpg?w=800&f=webp&q=85&bl=5'\r\n *\r\n * // Request original image (no transformations)\r\n * buildTransformationUrl(config, { original: true });\r\n * // => 'https://example.com/images/photo.jpg?original'\r\n *\r\n * // Using long parameter names\r\n * buildTransformationUrl(config, { width: 400 }, 'long');\r\n * // => 'https://example.com/images/photo.jpg?width=400'\r\n * ```\r\n */\r\nexport const buildTransformationUrl = (\r\n config: UrlBuilderConfig,\r\n options: TransformationOptions,\r\n mode: NameMode = 'short'\r\n) => {\r\n if (options.original) {\r\n return `${config.baseUrl}?original`;\r\n }\r\n\r\n const query = buildTransformationQueryParams(options, mode);\r\n\r\n const baseUrl = removeTrailingSlash(config.baseUrl)\r\n return `${baseUrl}?${query}`;\r\n};\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/src-set.ts","../src/utils.ts","../src/config.ts","../src/url-builder.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { buildTransformationUrl, NameMode } from './url-builder';\r\nimport { TransformationOptions, UrlBuilderConfig } from './types';\r\nimport { clampMax, nearest, parseVW } from './utils';\r\n\r\n/**\r\n * Maximum supported image width\r\n */\r\nexport const MAX_IMAGE_WIDTH = 4096;\r\n\r\n/**\r\n * Default device breakpoints for responsive srcset generation\r\n */\r\nexport const DEFAULT_DEVICE_BREAKPOINTS = [\r\n 320,\r\n 375,\r\n 640,\r\n 768,\r\n 1024,\r\n 1280,\r\n 1536,\r\n 1920\r\n] as const;\r\n\r\n/**\r\n * Default image breakpoints for smaller images (icons, thumbnails)\r\n */\r\nexport const DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 384] as const;\r\n\r\n/**\r\n * Options for responsive image attributes generation\r\n */\r\nexport interface ResponsiveImageOptions {\r\n /**\r\n * The `sizes` attribute value (e.g., \"100vw\", \"(max-width: 768px) 100vw, 50vw\").\r\n * When provided, generates width descriptors (`w`) for responsive layouts.\r\n */\r\n sizes?: string;\r\n /**\r\n * Fixed display width in CSS pixels.\r\n * When provided without `sizes`, generates DPR descriptors (`x`).\r\n */\r\n width?: number;\r\n /** Device breakpoints for viewport-based sizing (default: DEFAULT_DEVICE_BREAKPOINTS) */\r\n deviceBreakpoints?: number[];\r\n /** Image breakpoints for smaller images (default: DEFAULT_IMAGE_BREAKPOINTS) */\r\n imageBreakpoints?: number[];\r\n /** Base transformation options to apply to all variants */\r\n transformations?: TransformationOptions;\r\n /** Whether to use short or long parameter names (default: 'short') */\r\n mode?: NameMode;\r\n}\r\n\r\n/**\r\n * Result of responsive image attributes generation\r\n */\r\nexport interface ResponsiveImageResult {\r\n /** The src attribute value (largest/default variant) */\r\n src: string;\r\n /** The srcset attribute value */\r\n srcSet?: string;\r\n /** The sizes attribute value (for width descriptors) */\r\n sizes?: string;\r\n /** The width attribute value (when explicitly provided) */\r\n width?: number;\r\n}\r\n\r\n/**\r\n * Computes candidate widths and descriptor type based on input options.\r\n */\r\nconst DEFAULT_MAX_DPR = 2;\r\n\r\nfunction computeCandidateWidths(params: {\r\n allBreakpoints: number[];\r\n deviceBreakpoints: number[];\r\n explicitWidth?: number;\r\n sizesAttr?: string;\r\n}): { candidates: number[]; descriptor: 'w' | 'x' } {\r\n const {\r\n allBreakpoints,\r\n deviceBreakpoints,\r\n explicitWidth,\r\n sizesAttr\r\n } = params;\r\n\r\n // ---- Normalize & guard ----\r\n const sortedAll = [...new Set(allBreakpoints)].sort((a, b) => a - b);\r\n const sortedDevice = [...new Set(deviceBreakpoints)].sort((a, b) => a - b);\r\n\r\n if (!sortedAll.length || !sortedDevice.length) {\r\n return { candidates: [], descriptor: 'w' };\r\n }\r\n\r\n const minDevice = sortedDevice[0]!;\r\n const maxDevice = sortedDevice[sortedDevice.length - 1]!;\r\n\r\n const vwRatios = parseVW(sizesAttr);\r\n const hasVW = vwRatios.length > 0;\r\n\r\n // Explicit width (fixed layout) → x descriptors ----\r\n // Prefer this when layout width is known and sizes does NOT imply vw scaling\r\n if (typeof explicitWidth === 'number' && !hasVW) {\r\n const DPRs = [1, 2, 3];\r\n\r\n const candidates = Array.from(\r\n new Set(\r\n DPRs\r\n .map(dpr => nearest(sortedAll, explicitWidth * dpr))\r\n .map(clampMax)\r\n )\r\n ).filter(w => w <= MAX_IMAGE_WIDTH);\r\n\r\n return {\r\n candidates,\r\n descriptor: 'x'\r\n };\r\n }\r\n\r\n // Sizes with vw → w descriptors ----\r\n if (hasVW) {\r\n const minRatio = Math.min(...vwRatios);\r\n const maxRatio = Math.max(...vwRatios);\r\n\r\n const minPx = minDevice * minRatio;\r\n const maxPx = maxDevice * maxRatio * DEFAULT_MAX_DPR;\r\n\r\n const candidates = sortedAll.filter(\r\n w => w >= minPx && w <= clampMax(maxPx)\r\n );\r\n\r\n return {\r\n candidates,\r\n descriptor: 'w'\r\n };\r\n }\r\n\r\n // No sizes & no explicit width → device-based w ----\r\n const maxUseful = maxDevice * DEFAULT_MAX_DPR;\r\n\r\n return {\r\n candidates: sortedAll.filter(w => w <= clampMax(maxUseful)),\r\n descriptor: 'w'\r\n };\r\n}\r\n\r\n/**\r\n * Generates responsive image attributes (src, srcSet, sizes) for optimal loading.\r\n *\r\n * Supports two strategies:\r\n * 1. **Responsive (with `sizes`)**: Uses width descriptors (`w`) for fluid layouts\r\n * where image size varies with viewport.\r\n * 2. **Fixed width (with `width`)**: Uses DPR descriptors (`x`) for fixed-size\r\n * images that need high-DPI support.\r\n *\r\n * @param config - Configuration containing the base URL for the image\r\n * @param options - Options for responsive image generation\r\n * @returns Object containing src, srcSet, and optionally sizes attributes\r\n *\r\n * @example\r\n * ```ts\r\n * const config = { baseUrl: 'https://example.com/photo.jpg' };\r\n *\r\n * // Responsive image with sizes attribute (uses `w` descriptors)\r\n * createResponsiveAttributes(config, { sizes: '(max-width: 768px) 100vw, 50vw' });\r\n * // => {\r\n * // src: '...?w=3840',\r\n * // srcSet: '...?w=640 640w, ...?w=750 750w, ..., ...?w=3840 3840w',\r\n * // sizes: '(max-width: 768px) 100vw, 50vw'\r\n * // }\r\n *\r\n * // Fixed-width image (uses `x` descriptors)\r\n * createResponsiveAttributes(config, { width: 400 });\r\n * // => {\r\n * // src: '...?w=384',\r\n * // srcSet: '...?w=384 1x, ...?w=828 2x',\r\n * // width: 400\r\n * // }\r\n *\r\n * // With transformations\r\n * createResponsiveAttributes(config, {\r\n * sizes: '100vw',\r\n * transformations: { format: 'WEBP', quality: 80 }\r\n * });\r\n * ```\r\n */\r\nexport const createResponsiveAttributes = (\r\n config: UrlBuilderConfig,\r\n options: ResponsiveImageOptions = {}\r\n): ResponsiveImageResult => {\r\n const {\r\n sizes,\r\n width,\r\n deviceBreakpoints = [...DEFAULT_DEVICE_BREAKPOINTS],\r\n imageBreakpoints = [...DEFAULT_IMAGE_BREAKPOINTS],\r\n transformations = {},\r\n mode = 'short',\r\n } = options;\r\n\r\n const sortedDeviceBreakpoints = [...deviceBreakpoints].sort((a, b) => a - b);\r\n const sortedImageBreakpoints = [...imageBreakpoints].sort((a, b) => a - b);\r\n\r\n const allBreakpoints = [...new Set([...sortedImageBreakpoints, ...sortedDeviceBreakpoints])].sort((a, b) => a - b);\r\n\r\n const { candidates, descriptor } = computeCandidateWidths({\r\n allBreakpoints,\r\n deviceBreakpoints: sortedDeviceBreakpoints,\r\n ...(width !== undefined && { explicitWidth: width }),\r\n ...(sizes !== undefined && { sizesAttr: sizes }),\r\n });\r\n\r\n if (candidates.length === 0) {\r\n // Fallback: no valid candidates, return base URL\r\n return { src: config.baseUrl };\r\n }\r\n\r\n // Build srcSet entries\r\n const srcSetEntries = candidates.map((w, index,) => buildSrcEntries(w, index, config, transformations, mode, descriptor));\r\n\r\n // Default src is the largest candidate\r\n const largestWidth = candidates[candidates.length - 1]!;\r\n const src = buildTransformationUrl(\r\n config,\r\n { ...transformations, width: largestWidth },\r\n mode\r\n );\r\n\r\n const endSizes = sizes ?? (descriptor === 'w' ? '100vw' : undefined);\r\n\r\n return {\r\n src,\r\n ...(endSizes ? { sizes: endSizes } : {}),\r\n srcSet: srcSetEntries.join(', '),\r\n ...(width !== undefined ? { width } : {}),\r\n };\r\n};\r\n\r\nfunction buildSrcEntries(width: number, index: number, config: UrlBuilderConfig, transformations: TransformationOptions, mode: NameMode, descriptor: 'w' | 'x'): string {\r\n const url = buildTransformationUrl(\r\n config,\r\n { ...transformations, width: width },\r\n mode\r\n );\r\n\r\n const desc = descriptor === 'w' ? `${width}w` : `${index + 1}x`;\r\n\r\n return `${url} ${desc}`;\r\n}\r\n","import { Path, PathValue } from './util-types';\r\nimport { MAX_IMAGE_WIDTH } from './src-set';\r\n\r\nexport const normalizeHex = (value: string) => value.replace('#', '')\r\n\r\n\r\nexport const removeTrailingSlash = (baseUrl: string) => {\r\n return baseUrl.endsWith('/')\r\n ? baseUrl.slice(0, -1)\r\n : baseUrl;\r\n}\r\n\r\nexport const getNestedValue = <\r\n T,\r\n P extends Path<T>\r\n>(\r\n obj: T,\r\n path: P\r\n): PathValue<T, P> | undefined =>\r\n path.reduce<any>(\r\n (acc, key) => (acc == null ? undefined : acc[key]),\r\n obj\r\n );\r\n\r\n\r\nexport const clampMax = (v: number) => Math.min(v, MAX_IMAGE_WIDTH);\r\n\r\nexport const nearest = (sortedBreakpoints: number[],target: number) =>\r\n sortedBreakpoints.find(w => w >= target) ?? sortedBreakpoints[sortedBreakpoints.length - 1]!;\r\n\r\nexport const parseVW = (sizes?: string): number[] => {\r\n if (!sizes) return [];\r\n return [...sizes.matchAll(/(\\d+(?:\\.\\d+)?)vw/g)].map(\r\n m => Number(m[1]) / 100\r\n );\r\n};\r\n\r\n","import { ParamSpec, Path } from './util-types';\r\nimport { TransformationOptions } from './types';\r\nimport { normalizeHex } from './utils';\r\n\r\nexport const PARAMS = [\r\n {path: ['width'], long: 'width', short: 'w'},\r\n {path: ['height'], long: 'height', short: 'h'},\r\n {path: ['dpr'], long: 'dpr', short: 'dpr'},\r\n {path: ['mode'], long: 'mode', short: 'rm'},\r\n {\r\n path: ['background'],\r\n long: 'background',\r\n short: 'bg',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n\r\n {path: ['format'], long: 'format', short: 'f'},\r\n {path: ['quality'], long: 'quality', short: 'q'},\r\n {path: ['stripMetadata'], long: 'stripMetadata', short: 'st'},\r\n\r\n {path: ['blur'], long: 'blur', short: 'bl'},\r\n {path: ['brightness'], long: 'brightness', short: 'br'},\r\n {path: ['contrast'], long: 'contrast', short: 'ct'},\r\n {path: ['grayscale'], long: 'grayscale', short: 'gs'},\r\n {path: ['saturation'], long: 'saturation', short: 'sa'},\r\n {path: ['sepia'], long: 'sepia', short: 'sp'},\r\n {path: ['sharpen'], long: 'sharpen', short: 'sh'},\r\n {path: ['noise'], long: 'noise', short: 'ns'},\r\n {path: ['rotate'], long: 'rotate', short: 'rt'},\r\n\r\n {path: ['border', 'width'], long: 'border.width', short: 'bo.w'},\r\n {\r\n path: ['border', 'color'],\r\n long: 'border.color',\r\n short: 'bo.c',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['border', 'radius'], long: 'border.radius', short: 'bo.r'},\r\n\r\n {path: ['crop', 'type'], long: 'crop.type', short: 'cr.t'},\r\n {path: ['crop', 'objectType'], long: 'crop.objectType', short: 'cr.o'},\r\n {path: ['crop', 'focus', 'position', 'x'], long: 'crop.x', short: 'cr.x'},\r\n {path: ['crop', 'focus', 'position', 'y'], long: 'crop.y', short: 'cr.y'},\r\n {path: ['crop', 'focus', 'strategy'], long: 'crop', short: 'cr'},\r\n {path: ['crop', 'width'], long: 'crop.width', short: 'cr.w'},\r\n {path: ['crop', 'height'], long: 'crop.height', short: 'cr.h'},\r\n {path: ['crop', 'afterResize'], long: 'crop.afterResize', short: 'cr.ar'},\r\n\r\n {path: ['text', 'text'], long: 'text', short: 'tx'},\r\n {\r\n path: ['text', 'color'],\r\n long: 'text.color',\r\n short: 'tx.c',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['text', 'opacity'], long: 'text.opacity', short: 'tx.o'},\r\n {path: ['text', 'size'], long: 'text.size', short: 'tx.s'},\r\n {path: ['text', 'font'], long: 'text.font', short: 'tx.f'},\r\n {path: ['text', 'fontSize'], long: 'text.fontSize', short: 'tx.fs'},\r\n {path: ['text', 'position'], long: 'text.position', short: 'tx.p'},\r\n {\r\n path: ['text', 'background'],\r\n long: 'text.background',\r\n short: 'tx.bg',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['text', 'backgroundOpacity'], long: 'text.backgroundOpacity', short: 'tx.bg.o'},\r\n\r\n {path: ['watermark', 'name'], long: 'watermark', short: 'wm'},\r\n {path: ['watermark', 'opacity'], long: 'watermark.opacity', short: 'wm.o'},\r\n {path: ['watermark', 'size'], long: 'watermark.size', short: 'wm.s'},\r\n {path: ['watermark', 'width'], long: 'watermark.width', short: 'wm.w'},\r\n {path: ['watermark', 'height'], long: 'watermark.height', short: 'wm.h'},\r\n {path: ['watermark', 'position'], long: 'watermark.position', short: 'wm.p'},\r\n] as const satisfies readonly ParamSpec<\r\n TransformationOptions,\r\n Path<TransformationOptions>\r\n>[];\r\n","import { TransformationOptions, UrlBuilderConfig } from './types';\r\nimport { getNestedValue, removeTrailingSlash } from './utils';\r\nimport { PARAMS } from './config';\r\n\r\n/**\r\n * Determines which parameter names to use in the query string.\r\n * - `'short'` - Use abbreviated aliases (e.g., `w`, `h`, `q`)\r\n * - `'long'` - Use full parameter names (e.g., `width`, `height`, `quality`)\r\n */\r\nexport type NameMode = 'short' | 'long';\r\n\r\n/**\r\n * Converts transformation options into a URL-encoded query string.\r\n *\r\n * Iterates through all defined parameters in the PARAMS config, extracts values\r\n * from the options object using their paths, applies any normalization functions\r\n * (e.g., stripping `#` from hex colors), and returns a URL-encoded query string.\r\n *\r\n * @param options - The transformation options object\r\n * @param mode - Whether to use short aliases or long parameter names (default: `'short'`)\r\n * @returns URL-encoded query string (without leading `?`)\r\n *\r\n * @example\r\n * ```ts\r\n * // Using short aliases (default)\r\n * buildQueryParams({ width: 800, height: 600, quality: 80 });\r\n * // => 'w=800&h=600&q=80'\r\n *\r\n * // Using long parameter names\r\n * buildQueryParams({ width: 800, format: 'webp' }, 'long');\r\n * // => 'width=800&format=webp'\r\n *\r\n * // Nested options\r\n * buildQueryParams({ border: { width: 2, color: '#ff0000' } });\r\n * // => 'bo.w=2&bo.c=ff0000'\r\n * ```\r\n */\r\nexport const buildTransformationQueryParams = (\r\n options: TransformationOptions,\r\n mode: NameMode = 'short'\r\n): string => {\r\n const params: Array<[string, string]> = [];\r\n\r\n for (const spec of PARAMS) {\r\n const value = getNestedValue(options, spec.path);\r\n if (value == null) continue;\r\n\r\n const name =\r\n mode === 'short' && spec.short ? spec.short : spec.long;\r\n\r\n let normalized = value\r\n if ('normalize' in spec) {\r\n normalized = spec.normalize(value)\r\n }\r\n\r\n params.push([name, String(normalized)]);\r\n }\r\n\r\n\r\n return new URLSearchParams(\r\n params.map(([k, v]) => [k, v])\r\n ).toString();\r\n\r\n};\r\n\r\n/**\r\n * Builds a complete transformation URL from a base URL and transformation options.\r\n *\r\n * Combines the configured base URL with query parameters generated from the\r\n * transformation options. Handles the special `original` flag which returns\r\n * the unmodified source image.\r\n *\r\n * @param config - Configuration containing the base URL for the image\r\n * @param options - The transformation options to apply\r\n * @param mode - Whether to use short aliases or long parameter names (default: `'short'`)\r\n * @returns The complete URL string with query parameters\r\n *\r\n * @example\r\n * ```ts\r\n * const config = { baseUrl: 'https://example.com/images/photo.jpg' };\r\n *\r\n * // Basic resize\r\n * buildTransformationUrl(config, { width: 400, height: 300 });\r\n * // => 'https://example.com/images/photo.jpg?w=400&h=300'\r\n *\r\n * // Multiple transformations\r\n * buildTransformationUrl(config, {\r\n * width: 800,\r\n * format: 'webp',\r\n * quality: 85,\r\n * blur: 5\r\n * });\r\n * // => 'https://example.com/images/photo.jpg?w=800&f=webp&q=85&bl=5'\r\n *\r\n * // Request original image (no transformations)\r\n * buildTransformationUrl(config, { original: true });\r\n * // => 'https://example.com/images/photo.jpg?original'\r\n *\r\n * // Using long parameter names\r\n * buildTransformationUrl(config, { width: 400 }, 'long');\r\n * // => 'https://example.com/images/photo.jpg?width=400'\r\n * ```\r\n */\r\nexport const buildTransformationUrl = (\r\n config: UrlBuilderConfig,\r\n options: TransformationOptions,\r\n mode: NameMode = 'short'\r\n) => {\r\n if (options.original) {\r\n return `${config.baseUrl}?original`;\r\n }\r\n\r\n const query = buildTransformationQueryParams(options, mode);\r\n\r\n const baseUrl = removeTrailingSlash(config.baseUrl)\r\n return `${baseUrl}?${query}`;\r\n};\r\n"]}
|