@nonphoto/sanity-image 3.1.0 → 4.0.0
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/dist/index.d.ts +44 -68
- package/dist/index.js +116 -90
- package/package.json +1 -1
- package/src/asset.ts +102 -6
- package/src/constants.ts +19 -0
- package/src/crop.ts +6 -0
- package/src/image.ts +56 -0
- package/src/index.ts +5 -6
- package/src/{params.ts → options.ts} +23 -11
- package/src/rect.ts +21 -0
- package/src/imageObject.ts +0 -15
- package/src/metadata.ts +0 -35
- package/src/reference.ts +0 -12
- package/src/stub.ts +0 -44
- package/src/url.ts +0 -76
package/dist/index.d.ts
CHANGED
|
@@ -1,50 +1,44 @@
|
|
|
1
|
-
interface
|
|
1
|
+
interface AssetLike {
|
|
2
2
|
_id: string;
|
|
3
3
|
}
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
interface SanityReference {
|
|
4
|
+
interface Reference {
|
|
7
5
|
_ref: string;
|
|
8
6
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
interface ImageAsset {
|
|
8
|
+
_id: string;
|
|
9
|
+
assetId: string;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
extension: string;
|
|
13
13
|
}
|
|
14
|
-
|
|
14
|
+
type ImageAssetSource = {
|
|
15
|
+
asset: ImageAssetSource;
|
|
16
|
+
} | ImageAsset | AssetLike | Reference | string;
|
|
17
|
+
declare function isAssetLike(input: unknown): input is AssetLike;
|
|
18
|
+
declare function isReference(input: unknown): input is Reference;
|
|
19
|
+
declare function isImageAsset(input: unknown): input is ImageAsset;
|
|
20
|
+
declare function hasAssetProp(input: unknown): input is {
|
|
21
|
+
asset: NonNullable<unknown>;
|
|
22
|
+
};
|
|
23
|
+
declare function imageIdFromSource(source: ImageAssetSource): string;
|
|
24
|
+
declare function imageIdFromUnknown(input: unknown): string | undefined;
|
|
25
|
+
declare function imageAsset(id: string): ImageAsset | undefined;
|
|
26
|
+
declare function imageAssetFromUnknown(input: unknown): ImageAsset | undefined;
|
|
15
27
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
lightMuted?: SanityImagePaletteSwatch | null;
|
|
29
|
-
lightVibrant?: SanityImagePaletteSwatch | null;
|
|
30
|
-
muted?: SanityImagePaletteSwatch | null;
|
|
31
|
-
vibrant?: SanityImagePaletteSwatch | null;
|
|
32
|
-
}
|
|
33
|
-
interface SanityImageDimensions {
|
|
34
|
-
_type?: "sanity.imageDimensions" | null;
|
|
35
|
-
aspectRatio?: number | null;
|
|
36
|
-
width?: number | null;
|
|
37
|
-
height?: number | null;
|
|
38
|
-
}
|
|
39
|
-
interface SanityImageMetadata {
|
|
40
|
-
_type: "sanity.imageMetadata";
|
|
41
|
-
blurHash?: string | null;
|
|
42
|
-
dimensions?: SanityImageDimensions | null;
|
|
43
|
-
hasAlpha?: boolean | null;
|
|
44
|
-
isOpaque?: boolean | null;
|
|
45
|
-
lqip?: string | null;
|
|
46
|
-
palette?: SanityImagePalette | null;
|
|
28
|
+
declare const defaultSrcsetWidths: number[];
|
|
29
|
+
|
|
30
|
+
type Crop = {
|
|
31
|
+
top?: number;
|
|
32
|
+
bottom?: number;
|
|
33
|
+
left?: number;
|
|
34
|
+
right?: number;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
interface RectLike {
|
|
38
|
+
pos: ArrayLike<number>;
|
|
39
|
+
size: ArrayLike<number>;
|
|
47
40
|
}
|
|
41
|
+
declare function rectFromCrop(asset: Pick<ImageAsset, "width" | "height">, crop: Crop): RectLike;
|
|
48
42
|
|
|
49
43
|
type ImageFormat = "jpg" | "pjpg" | "png" | "webp";
|
|
50
44
|
type FitMode = "clip" | "crop" | "fill" | "fillmax" | "max" | "scale" | "min";
|
|
@@ -52,7 +46,7 @@ type CropMode = "top" | "bottom" | "left" | "right" | "center" | "focalpoint" |
|
|
|
52
46
|
type AutoMode = "format";
|
|
53
47
|
type Orientation = 0 | 90 | 180 | 270;
|
|
54
48
|
type Dpr = 1 | 2 | 3;
|
|
55
|
-
type
|
|
49
|
+
type ImageOptions = {
|
|
56
50
|
auto?: AutoMode;
|
|
57
51
|
background?: string;
|
|
58
52
|
blur?: number;
|
|
@@ -62,10 +56,7 @@ type SanityImageParams = {
|
|
|
62
56
|
fit?: FitMode;
|
|
63
57
|
flipHorizontal?: boolean;
|
|
64
58
|
flipVertical?: boolean;
|
|
65
|
-
focalPoint?:
|
|
66
|
-
x: number;
|
|
67
|
-
y: number;
|
|
68
|
-
};
|
|
59
|
+
focalPoint?: ArrayLike<number>;
|
|
69
60
|
format?: ImageFormat;
|
|
70
61
|
frame?: number;
|
|
71
62
|
height?: number;
|
|
@@ -77,36 +68,21 @@ type SanityImageParams = {
|
|
|
77
68
|
orientation?: Orientation;
|
|
78
69
|
pad?: number;
|
|
79
70
|
quality?: number;
|
|
80
|
-
rect?:
|
|
81
|
-
left: number;
|
|
82
|
-
top: number;
|
|
83
|
-
width: number;
|
|
84
|
-
height: number;
|
|
85
|
-
};
|
|
71
|
+
rect?: RectLike;
|
|
86
72
|
saturation?: number;
|
|
87
73
|
sharpen?: number;
|
|
88
74
|
width?: number;
|
|
89
75
|
};
|
|
90
|
-
declare function
|
|
91
|
-
|
|
92
|
-
type SanityImageSource = SanityImageObject | SanityReference | SanityImageAssetLike | string;
|
|
93
|
-
interface SanityImageAssetStub {
|
|
94
|
-
id: string;
|
|
95
|
-
width: number;
|
|
96
|
-
height: number;
|
|
97
|
-
format: string;
|
|
98
|
-
vanityName?: string;
|
|
99
|
-
}
|
|
100
|
-
declare function parseSanityImageAssetId(assetId: string): SanityImageAssetStub | undefined;
|
|
101
|
-
declare function sanityImageAssetId(source: SanityImageSource): string;
|
|
102
|
-
declare function sanityImageAssetStub(source: SanityImageSource): SanityImageAssetStub | undefined;
|
|
76
|
+
declare function imageOptionsToSearchParamEntries({ auto, background, blur, crop, download, dpr, fit, flipHorizontal, flipVertical, focalPoint, format, frame, height, invert, maxHeight, maxWidth, minHeight, minWidth, orientation, pad, quality, rect, saturation, sharpen, width, }: ImageOptions): string[][];
|
|
103
77
|
|
|
104
|
-
declare const defaultSrcsetWidths: number[];
|
|
105
78
|
interface SanityClientLike {
|
|
106
79
|
projectId: string;
|
|
107
80
|
dataset: string;
|
|
108
81
|
}
|
|
109
|
-
|
|
110
|
-
|
|
82
|
+
interface ImageOptionsWithVanityName extends ImageOptions {
|
|
83
|
+
vanityName?: string;
|
|
84
|
+
}
|
|
85
|
+
declare function imageUrl(client: SanityClientLike, asset: ImageAsset, options?: ImageOptionsWithVanityName): string | undefined;
|
|
86
|
+
declare function imageSrcset(client: SanityClientLike, asset: ImageAsset, params?: (width: number) => Omit<ImageOptionsWithVanityName, "width">, widths?: number[]): string | undefined;
|
|
111
87
|
|
|
112
|
-
export { type
|
|
88
|
+
export { type AssetLike, type AutoMode, type Crop, type CropMode, type Dpr, type FitMode, type ImageAsset, type ImageAssetSource, type ImageFormat, type ImageOptions, type ImageOptionsWithVanityName, type Orientation, type RectLike, type Reference, type SanityClientLike, defaultSrcsetWidths, hasAssetProp, imageAsset, imageAssetFromUnknown, imageIdFromSource, imageIdFromUnknown, imageOptionsToSearchParamEntries, imageSrcset, imageUrl, isAssetLike, isImageAsset, isReference, rectFromCrop };
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,83 @@
|
|
|
1
1
|
// src/asset.ts
|
|
2
|
-
function
|
|
3
|
-
return
|
|
2
|
+
function isAssetLike(input) {
|
|
3
|
+
return input != null && typeof input === "object" && "_id" in input && typeof input._id === "string";
|
|
4
4
|
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
function isSanityReference(x) {
|
|
8
|
-
return x != null && typeof x === "object" && "_ref" in x && typeof x._ref === "string";
|
|
5
|
+
function isReference(input) {
|
|
6
|
+
return input != null && typeof input === "object" && "_ref" in input && typeof input._ref === "string";
|
|
9
7
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
function isImageAsset(input) {
|
|
9
|
+
return input != null && typeof input === "object" && "assetId" in input && typeof input.assetId === "string" && "width" in input && typeof input.width === "number" && "height" in input && typeof input.height === "number" && "extension" in input && typeof input.extension === "string";
|
|
10
|
+
}
|
|
11
|
+
function hasAssetProp(input) {
|
|
12
|
+
return input != null && typeof input === "object" && "asset" in input;
|
|
14
13
|
}
|
|
14
|
+
function imageIdFromSource(source) {
|
|
15
|
+
return typeof source === "string" ? source : isReference(source) ? source._ref : isAssetLike(source) ? source._id : imageIdFromSource(source.asset);
|
|
16
|
+
}
|
|
17
|
+
function imageIdFromUnknown(input) {
|
|
18
|
+
return typeof input === "string" ? input : isReference(input) ? input._ref : isAssetLike(input) ? input._id : hasAssetProp(input) ? imageIdFromUnknown(input.asset) : void 0;
|
|
19
|
+
}
|
|
20
|
+
function imageAsset(id) {
|
|
21
|
+
const matches = id.match(/^image-(\w+)-(\d+)x(\d+)-(\w+)$/);
|
|
22
|
+
if (matches) {
|
|
23
|
+
const [, assetId, width, height, extension] = matches;
|
|
24
|
+
return {
|
|
25
|
+
_id: id,
|
|
26
|
+
assetId,
|
|
27
|
+
width: Number(width),
|
|
28
|
+
height: Number(height),
|
|
29
|
+
extension
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function imageAssetFromUnknown(input) {
|
|
34
|
+
if (isImageAsset(input)) {
|
|
35
|
+
return input;
|
|
36
|
+
} else {
|
|
37
|
+
const id = imageIdFromUnknown(input);
|
|
38
|
+
return id ? imageAsset(id) : void 0;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/constants.ts
|
|
43
|
+
var defaultSrcsetWidths = [
|
|
44
|
+
6016,
|
|
45
|
+
// 6K
|
|
46
|
+
5120,
|
|
47
|
+
// 5K
|
|
48
|
+
4480,
|
|
49
|
+
// 4.5K
|
|
50
|
+
3840,
|
|
51
|
+
// 4K
|
|
52
|
+
3200,
|
|
53
|
+
// QHD+
|
|
54
|
+
2560,
|
|
55
|
+
// WQXGA
|
|
56
|
+
2048,
|
|
57
|
+
// QXGA
|
|
58
|
+
1920,
|
|
59
|
+
// 1080p
|
|
60
|
+
1668,
|
|
61
|
+
// iPad
|
|
62
|
+
1280,
|
|
63
|
+
// 720p
|
|
64
|
+
1080,
|
|
65
|
+
// iPhone 6-8 Plus
|
|
66
|
+
960,
|
|
67
|
+
720,
|
|
68
|
+
// iPhone 6-8
|
|
69
|
+
640,
|
|
70
|
+
// 480p
|
|
71
|
+
480,
|
|
72
|
+
360,
|
|
73
|
+
240
|
|
74
|
+
];
|
|
15
75
|
|
|
16
|
-
// src/
|
|
17
|
-
function
|
|
76
|
+
// src/options.ts
|
|
77
|
+
function isValidEntry(entry) {
|
|
78
|
+
return typeof entry[0] === "string" && ["string", "number", "boolean"].includes(typeof entry[1]);
|
|
79
|
+
}
|
|
80
|
+
function imageOptionsToSearchParamEntries({
|
|
18
81
|
auto,
|
|
19
82
|
background,
|
|
20
83
|
blur,
|
|
@@ -51,8 +114,8 @@ function sanityImageParamsToSearchParamEntries({
|
|
|
51
114
|
fit,
|
|
52
115
|
flip: [flipHorizontal && "h", flipVertical && "v"].filter(Boolean).join(""),
|
|
53
116
|
fm: format,
|
|
54
|
-
"fp-x": focalPoint?.
|
|
55
|
-
"fp-y": focalPoint?.
|
|
117
|
+
"fp-x": focalPoint?.[0],
|
|
118
|
+
"fp-y": focalPoint?.[1],
|
|
56
119
|
frame,
|
|
57
120
|
h: height,
|
|
58
121
|
invert,
|
|
@@ -63,107 +126,70 @@ function sanityImageParamsToSearchParamEntries({
|
|
|
63
126
|
or: orientation,
|
|
64
127
|
pad,
|
|
65
128
|
q: quality,
|
|
66
|
-
rect: rect ? [rect.
|
|
129
|
+
rect: rect ? [rect.pos[0], rect.pos[1], rect.size[0], rect.size[1]].map(Math.round).join(",") : void 0,
|
|
67
130
|
sat: saturation,
|
|
68
131
|
sharp: sharpen,
|
|
69
132
|
w: width
|
|
70
|
-
}).filter(([, value]) => typeof value === "number" || Boolean(value)).map(([key, value]) => [
|
|
133
|
+
}).filter(isValidEntry).filter(([, value]) => typeof value === "number" || Boolean(value)).map(([key, value]) => [
|
|
71
134
|
key,
|
|
72
|
-
encodeURIComponent(
|
|
73
|
-
typeof value === "number" ? Math.round(value) : value
|
|
74
|
-
)
|
|
135
|
+
encodeURIComponent(typeof value === "number" ? Math.round(value) : value)
|
|
75
136
|
]);
|
|
76
137
|
}
|
|
77
138
|
|
|
78
|
-
// src/
|
|
79
|
-
function
|
|
80
|
-
const matches = assetId.match(/^image-(\w+)-(\d+)x(\d+)-(\w+)$/);
|
|
81
|
-
if (matches) {
|
|
82
|
-
const [, id, width, height, format] = matches;
|
|
83
|
-
return { id, width: Number(width), height: Number(height), format };
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
function sanityImageAssetId(source) {
|
|
87
|
-
return typeof source === "string" ? source : isSanityReference(source) ? source._ref : isSanityImageAssetLike(source) ? source._id : sanityImageAssetId(source.asset);
|
|
88
|
-
}
|
|
89
|
-
function sanityImageAssetStub(source) {
|
|
90
|
-
const id = sanityImageAssetId(source);
|
|
91
|
-
return id ? parseSanityImageAssetId(id) : void 0;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// src/url.ts
|
|
95
|
-
var defaultSrcsetWidths = [
|
|
96
|
-
6016,
|
|
97
|
-
// 6K
|
|
98
|
-
5120,
|
|
99
|
-
// 5K
|
|
100
|
-
4480,
|
|
101
|
-
// 4.5K
|
|
102
|
-
3840,
|
|
103
|
-
// 4K
|
|
104
|
-
3200,
|
|
105
|
-
// QHD+
|
|
106
|
-
2560,
|
|
107
|
-
// WQXGA
|
|
108
|
-
2048,
|
|
109
|
-
// QXGA
|
|
110
|
-
1920,
|
|
111
|
-
// 1080p
|
|
112
|
-
1668,
|
|
113
|
-
// iPad
|
|
114
|
-
1280,
|
|
115
|
-
// 720p
|
|
116
|
-
1080,
|
|
117
|
-
// iPhone 6-8 Plus
|
|
118
|
-
960,
|
|
119
|
-
720,
|
|
120
|
-
// iPhone 6-8
|
|
121
|
-
640,
|
|
122
|
-
// 480p
|
|
123
|
-
480,
|
|
124
|
-
360,
|
|
125
|
-
240
|
|
126
|
-
];
|
|
127
|
-
function sanityImageUrl(client, image, params) {
|
|
139
|
+
// src/image.ts
|
|
140
|
+
function imageUrl(client, asset, options) {
|
|
128
141
|
const url = new URL(
|
|
129
142
|
[
|
|
130
143
|
`https://cdn.sanity.io/images`,
|
|
131
144
|
client.projectId,
|
|
132
145
|
client.dataset,
|
|
133
|
-
`${
|
|
134
|
-
|
|
146
|
+
`${asset.assetId}-${asset.width}x${asset.height}.${asset.extension}`,
|
|
147
|
+
options?.vanityName
|
|
135
148
|
].filter(Boolean).join("/")
|
|
136
149
|
);
|
|
137
|
-
if (
|
|
150
|
+
if (options) {
|
|
138
151
|
url.search = new URLSearchParams(
|
|
139
|
-
|
|
152
|
+
imageOptionsToSearchParamEntries(options)
|
|
140
153
|
).toString();
|
|
141
154
|
}
|
|
142
155
|
return url.href;
|
|
143
156
|
}
|
|
144
|
-
function
|
|
145
|
-
const aspectRatio = image.height / image.width;
|
|
157
|
+
function imageSrcset(client, asset, params, widths = defaultSrcsetWidths) {
|
|
146
158
|
return [
|
|
147
|
-
...widths.sort((a, b) => a - b).filter((width) => width <
|
|
148
|
-
|
|
159
|
+
...widths.sort((a, b) => a - b).filter((width) => width < asset.width),
|
|
160
|
+
asset.width
|
|
149
161
|
].map((width) => {
|
|
150
|
-
const url =
|
|
151
|
-
...params,
|
|
152
|
-
width
|
|
153
|
-
height: width * aspectRatio
|
|
162
|
+
const url = imageUrl(client, asset, {
|
|
163
|
+
...params?.(width),
|
|
164
|
+
width
|
|
154
165
|
});
|
|
155
166
|
return `${url} ${width}w`;
|
|
156
167
|
}).join(",");
|
|
157
168
|
}
|
|
169
|
+
|
|
170
|
+
// src/rect.ts
|
|
171
|
+
function rectFromCrop(asset, crop) {
|
|
172
|
+
const left = crop.left ?? 0;
|
|
173
|
+
const right = crop.right ?? 0;
|
|
174
|
+
const top = crop.top ?? 0;
|
|
175
|
+
const bottom = crop.bottom ?? 0;
|
|
176
|
+
return {
|
|
177
|
+
pos: [left * asset.width, right * asset.width],
|
|
178
|
+
size: [(1 - left - right) * asset.width, (1 - top - bottom) * asset.height]
|
|
179
|
+
};
|
|
180
|
+
}
|
|
158
181
|
export {
|
|
159
182
|
defaultSrcsetWidths,
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
183
|
+
hasAssetProp,
|
|
184
|
+
imageAsset,
|
|
185
|
+
imageAssetFromUnknown,
|
|
186
|
+
imageIdFromSource,
|
|
187
|
+
imageIdFromUnknown,
|
|
188
|
+
imageOptionsToSearchParamEntries,
|
|
189
|
+
imageSrcset,
|
|
190
|
+
imageUrl,
|
|
191
|
+
isAssetLike,
|
|
192
|
+
isImageAsset,
|
|
193
|
+
isReference,
|
|
194
|
+
rectFromCrop
|
|
169
195
|
};
|
package/package.json
CHANGED
package/src/asset.ts
CHANGED
|
@@ -1,12 +1,108 @@
|
|
|
1
|
-
export interface
|
|
1
|
+
export interface AssetLike {
|
|
2
2
|
_id: string;
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
export interface Reference {
|
|
6
|
+
_ref: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ImageAsset {
|
|
10
|
+
_id: string;
|
|
11
|
+
assetId: string;
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
extension: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type ImageAssetSource =
|
|
18
|
+
| {
|
|
19
|
+
asset: ImageAssetSource;
|
|
20
|
+
}
|
|
21
|
+
| ImageAsset
|
|
22
|
+
| AssetLike
|
|
23
|
+
| Reference
|
|
24
|
+
| string;
|
|
25
|
+
|
|
26
|
+
export function isAssetLike(input: unknown): input is AssetLike {
|
|
27
|
+
return (
|
|
28
|
+
input != null &&
|
|
29
|
+
typeof input === "object" &&
|
|
30
|
+
"_id" in input &&
|
|
31
|
+
typeof input._id === "string"
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function isReference(input: unknown): input is Reference {
|
|
36
|
+
return (
|
|
37
|
+
input != null &&
|
|
38
|
+
typeof input === "object" &&
|
|
39
|
+
"_ref" in input &&
|
|
40
|
+
typeof input._ref === "string"
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function isImageAsset(input: unknown): input is ImageAsset {
|
|
6
45
|
return (
|
|
7
|
-
|
|
8
|
-
typeof
|
|
9
|
-
"
|
|
10
|
-
typeof
|
|
46
|
+
input != null &&
|
|
47
|
+
typeof input === "object" &&
|
|
48
|
+
"assetId" in input &&
|
|
49
|
+
typeof input.assetId === "string" &&
|
|
50
|
+
"width" in input &&
|
|
51
|
+
typeof input.width === "number" &&
|
|
52
|
+
"height" in input &&
|
|
53
|
+
typeof input.height === "number" &&
|
|
54
|
+
"extension" in input &&
|
|
55
|
+
typeof input.extension === "string"
|
|
11
56
|
);
|
|
12
57
|
}
|
|
58
|
+
|
|
59
|
+
export function hasAssetProp(
|
|
60
|
+
input: unknown,
|
|
61
|
+
): input is { asset: NonNullable<unknown> } {
|
|
62
|
+
return input != null && typeof input === "object" && "asset" in input;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function imageIdFromSource(source: ImageAssetSource): string {
|
|
66
|
+
return typeof source === "string"
|
|
67
|
+
? source
|
|
68
|
+
: isReference(source)
|
|
69
|
+
? source._ref
|
|
70
|
+
: isAssetLike(source)
|
|
71
|
+
? source._id
|
|
72
|
+
: imageIdFromSource(source.asset);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function imageIdFromUnknown(input: unknown): string | undefined {
|
|
76
|
+
return typeof input === "string"
|
|
77
|
+
? input
|
|
78
|
+
: isReference(input)
|
|
79
|
+
? input._ref
|
|
80
|
+
: isAssetLike(input)
|
|
81
|
+
? input._id
|
|
82
|
+
: hasAssetProp(input)
|
|
83
|
+
? imageIdFromUnknown(input.asset)
|
|
84
|
+
: undefined;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function imageAsset(id: string): ImageAsset | undefined {
|
|
88
|
+
const matches = id.match(/^image-(\w+)-(\d+)x(\d+)-(\w+)$/);
|
|
89
|
+
if (matches) {
|
|
90
|
+
const [, assetId, width, height, extension] = matches;
|
|
91
|
+
return {
|
|
92
|
+
_id: id,
|
|
93
|
+
assetId,
|
|
94
|
+
width: Number(width),
|
|
95
|
+
height: Number(height),
|
|
96
|
+
extension,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function imageAssetFromUnknown(input: unknown): ImageAsset | undefined {
|
|
102
|
+
if (isImageAsset(input)) {
|
|
103
|
+
return input;
|
|
104
|
+
} else {
|
|
105
|
+
const id = imageIdFromUnknown(input);
|
|
106
|
+
return id ? imageAsset(id) : undefined;
|
|
107
|
+
}
|
|
108
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const defaultSrcsetWidths = [
|
|
2
|
+
6016, // 6K
|
|
3
|
+
5120, // 5K
|
|
4
|
+
4480, // 4.5K
|
|
5
|
+
3840, // 4K
|
|
6
|
+
3200, // QHD+
|
|
7
|
+
2560, // WQXGA
|
|
8
|
+
2048, // QXGA
|
|
9
|
+
1920, // 1080p
|
|
10
|
+
1668, // iPad
|
|
11
|
+
1280, // 720p
|
|
12
|
+
1080, // iPhone 6-8 Plus
|
|
13
|
+
960,
|
|
14
|
+
720, // iPhone 6-8
|
|
15
|
+
640, // 480p
|
|
16
|
+
480,
|
|
17
|
+
360,
|
|
18
|
+
240,
|
|
19
|
+
];
|
package/src/crop.ts
ADDED
package/src/image.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ImageAsset } from "./asset";
|
|
2
|
+
import { defaultSrcsetWidths } from "./constants";
|
|
3
|
+
import { ImageOptions, imageOptionsToSearchParamEntries } from "./options";
|
|
4
|
+
|
|
5
|
+
export interface SanityClientLike {
|
|
6
|
+
projectId: string;
|
|
7
|
+
dataset: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ImageOptionsWithVanityName extends ImageOptions {
|
|
11
|
+
vanityName?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function imageUrl(
|
|
15
|
+
client: SanityClientLike,
|
|
16
|
+
asset: ImageAsset,
|
|
17
|
+
options?: ImageOptionsWithVanityName,
|
|
18
|
+
): string | undefined {
|
|
19
|
+
const url = new URL(
|
|
20
|
+
[
|
|
21
|
+
`https://cdn.sanity.io/images`,
|
|
22
|
+
client.projectId,
|
|
23
|
+
client.dataset,
|
|
24
|
+
`${asset.assetId}-${asset.width}x${asset.height}.${asset.extension}`,
|
|
25
|
+
options?.vanityName,
|
|
26
|
+
]
|
|
27
|
+
.filter(Boolean)
|
|
28
|
+
.join("/"),
|
|
29
|
+
);
|
|
30
|
+
if (options) {
|
|
31
|
+
url.search = new URLSearchParams(
|
|
32
|
+
imageOptionsToSearchParamEntries(options),
|
|
33
|
+
).toString();
|
|
34
|
+
}
|
|
35
|
+
return url.href;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function imageSrcset(
|
|
39
|
+
client: SanityClientLike,
|
|
40
|
+
asset: ImageAsset,
|
|
41
|
+
params?: (width: number) => Omit<ImageOptionsWithVanityName, "width">,
|
|
42
|
+
widths: number[] = defaultSrcsetWidths,
|
|
43
|
+
): string | undefined {
|
|
44
|
+
return [
|
|
45
|
+
...widths.sort((a, b) => a - b).filter((width) => width < asset.width),
|
|
46
|
+
asset.width,
|
|
47
|
+
]
|
|
48
|
+
.map((width) => {
|
|
49
|
+
const url = imageUrl(client, asset, {
|
|
50
|
+
...params?.(width),
|
|
51
|
+
width,
|
|
52
|
+
});
|
|
53
|
+
return `${url} ${width}w`;
|
|
54
|
+
})
|
|
55
|
+
.join(",");
|
|
56
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export * from "./asset";
|
|
2
|
-
export * from "./
|
|
3
|
-
export * from "./
|
|
4
|
-
export * from "./
|
|
5
|
-
export * from "./
|
|
6
|
-
export * from "./
|
|
7
|
-
export * from "./url";
|
|
2
|
+
export * from "./constants";
|
|
3
|
+
export * from "./crop";
|
|
4
|
+
export * from "./image";
|
|
5
|
+
export * from "./options";
|
|
6
|
+
export * from "./rect";
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { RectLike } from "./rect";
|
|
2
|
+
|
|
1
3
|
export type ImageFormat = "jpg" | "pjpg" | "png" | "webp";
|
|
2
4
|
|
|
3
5
|
export type FitMode =
|
|
@@ -24,7 +26,7 @@ export type Orientation = 0 | 90 | 180 | 270;
|
|
|
24
26
|
|
|
25
27
|
export type Dpr = 1 | 2 | 3;
|
|
26
28
|
|
|
27
|
-
export type
|
|
29
|
+
export type ImageOptions = {
|
|
28
30
|
auto?: AutoMode;
|
|
29
31
|
background?: string;
|
|
30
32
|
blur?: number;
|
|
@@ -34,7 +36,7 @@ export type SanityImageParams = {
|
|
|
34
36
|
fit?: FitMode;
|
|
35
37
|
flipHorizontal?: boolean;
|
|
36
38
|
flipVertical?: boolean;
|
|
37
|
-
focalPoint?:
|
|
39
|
+
focalPoint?: ArrayLike<number>;
|
|
38
40
|
format?: ImageFormat;
|
|
39
41
|
frame?: number;
|
|
40
42
|
height?: number;
|
|
@@ -46,13 +48,22 @@ export type SanityImageParams = {
|
|
|
46
48
|
orientation?: Orientation;
|
|
47
49
|
pad?: number;
|
|
48
50
|
quality?: number;
|
|
49
|
-
rect?:
|
|
51
|
+
rect?: RectLike;
|
|
50
52
|
saturation?: number;
|
|
51
53
|
sharpen?: number;
|
|
52
54
|
width?: number;
|
|
53
55
|
};
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
type ValidEntry = [string, string | number | boolean];
|
|
58
|
+
|
|
59
|
+
function isValidEntry(entry: [unknown, unknown]): entry is ValidEntry {
|
|
60
|
+
return (
|
|
61
|
+
typeof entry[0] === "string" &&
|
|
62
|
+
["string", "number", "boolean"].includes(typeof entry[1])
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function imageOptionsToSearchParamEntries({
|
|
56
67
|
auto,
|
|
57
68
|
background,
|
|
58
69
|
blur,
|
|
@@ -78,7 +89,7 @@ export function sanityImageParamsToSearchParamEntries({
|
|
|
78
89
|
saturation,
|
|
79
90
|
sharpen,
|
|
80
91
|
width,
|
|
81
|
-
}:
|
|
92
|
+
}: ImageOptions): string[][] {
|
|
82
93
|
return Object.entries({
|
|
83
94
|
auto,
|
|
84
95
|
bg: background,
|
|
@@ -89,8 +100,8 @@ export function sanityImageParamsToSearchParamEntries({
|
|
|
89
100
|
fit,
|
|
90
101
|
flip: [flipHorizontal && "h", flipVertical && "v"].filter(Boolean).join(""),
|
|
91
102
|
fm: format,
|
|
92
|
-
"fp-x": focalPoint?.
|
|
93
|
-
"fp-y": focalPoint?.
|
|
103
|
+
"fp-x": focalPoint?.[0],
|
|
104
|
+
"fp-y": focalPoint?.[1],
|
|
94
105
|
frame,
|
|
95
106
|
h: height,
|
|
96
107
|
invert,
|
|
@@ -102,17 +113,18 @@ export function sanityImageParamsToSearchParamEntries({
|
|
|
102
113
|
pad,
|
|
103
114
|
q: quality,
|
|
104
115
|
rect: rect
|
|
105
|
-
? [rect.
|
|
116
|
+
? [rect.pos[0], rect.pos[1], rect.size[0], rect.size[1]]
|
|
117
|
+
.map(Math.round)
|
|
118
|
+
.join(",")
|
|
106
119
|
: undefined,
|
|
107
120
|
sat: saturation,
|
|
108
121
|
sharp: sharpen,
|
|
109
122
|
w: width,
|
|
110
123
|
})
|
|
124
|
+
.filter(isValidEntry)
|
|
111
125
|
.filter(([, value]) => typeof value === "number" || Boolean(value))
|
|
112
126
|
.map(([key, value]) => [
|
|
113
127
|
key,
|
|
114
|
-
encodeURIComponent(
|
|
115
|
-
typeof value === "number" ? Math.round(value) : (value as string)
|
|
116
|
-
),
|
|
128
|
+
encodeURIComponent(typeof value === "number" ? Math.round(value) : value),
|
|
117
129
|
]);
|
|
118
130
|
}
|
package/src/rect.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ImageAsset } from "./asset";
|
|
2
|
+
import { Crop } from "./crop";
|
|
3
|
+
|
|
4
|
+
export interface RectLike {
|
|
5
|
+
pos: ArrayLike<number>;
|
|
6
|
+
size: ArrayLike<number>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function rectFromCrop(
|
|
10
|
+
asset: Pick<ImageAsset, "width" | "height">,
|
|
11
|
+
crop: Crop,
|
|
12
|
+
): RectLike {
|
|
13
|
+
const left = crop.left ?? 0;
|
|
14
|
+
const right = crop.right ?? 0;
|
|
15
|
+
const top = crop.top ?? 0;
|
|
16
|
+
const bottom = crop.bottom ?? 0;
|
|
17
|
+
return {
|
|
18
|
+
pos: [left * asset.width, right * asset.width],
|
|
19
|
+
size: [(1 - left - right) * asset.width, (1 - top - bottom) * asset.height],
|
|
20
|
+
};
|
|
21
|
+
}
|
package/src/imageObject.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { isSanityImageAssetLike, SanityImageAssetLike } from "./asset";
|
|
2
|
-
import { isSanityReference, SanityReference } from "./reference";
|
|
3
|
-
|
|
4
|
-
export interface SanityImageObject {
|
|
5
|
-
asset: SanityImageAssetLike | SanityReference;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function isSanityImageObject(x: any): x is SanityImageObject {
|
|
9
|
-
return (
|
|
10
|
-
x != null &&
|
|
11
|
-
typeof x === "object" &&
|
|
12
|
-
"asset" in x &&
|
|
13
|
-
(isSanityImageAssetLike(x.asset) || isSanityReference(x.asset))
|
|
14
|
-
);
|
|
15
|
-
}
|
package/src/metadata.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
export interface SanityImagePaletteSwatch {
|
|
2
|
-
_type?: "sanity.imagePaletteSwatch" | null;
|
|
3
|
-
background?: string | null;
|
|
4
|
-
foreground?: string | null;
|
|
5
|
-
population?: number | null;
|
|
6
|
-
title?: string | null;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface SanityImagePalette {
|
|
10
|
-
_type?: "sanity.imagePalette" | null;
|
|
11
|
-
darkMuted?: SanityImagePaletteSwatch | null;
|
|
12
|
-
darkVibrant?: SanityImagePaletteSwatch | null;
|
|
13
|
-
dominant?: SanityImagePaletteSwatch | null;
|
|
14
|
-
lightMuted?: SanityImagePaletteSwatch | null;
|
|
15
|
-
lightVibrant?: SanityImagePaletteSwatch | null;
|
|
16
|
-
muted?: SanityImagePaletteSwatch | null;
|
|
17
|
-
vibrant?: SanityImagePaletteSwatch | null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface SanityImageDimensions {
|
|
21
|
-
_type?: "sanity.imageDimensions" | null;
|
|
22
|
-
aspectRatio?: number | null;
|
|
23
|
-
width?: number | null;
|
|
24
|
-
height?: number | null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface SanityImageMetadata {
|
|
28
|
-
_type: "sanity.imageMetadata";
|
|
29
|
-
blurHash?: string | null;
|
|
30
|
-
dimensions?: SanityImageDimensions | null;
|
|
31
|
-
hasAlpha?: boolean | null;
|
|
32
|
-
isOpaque?: boolean | null;
|
|
33
|
-
lqip?: string | null;
|
|
34
|
-
palette?: SanityImagePalette | null;
|
|
35
|
-
}
|
package/src/reference.ts
DELETED
package/src/stub.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { isSanityImageAssetLike, SanityImageAssetLike } from "./asset";
|
|
2
|
-
import { SanityImageObject } from "./imageObject";
|
|
3
|
-
import { isSanityReference, SanityReference } from "./reference";
|
|
4
|
-
|
|
5
|
-
export type SanityImageSource =
|
|
6
|
-
| SanityImageObject
|
|
7
|
-
| SanityReference
|
|
8
|
-
| SanityImageAssetLike
|
|
9
|
-
| string;
|
|
10
|
-
|
|
11
|
-
export interface SanityImageAssetStub {
|
|
12
|
-
id: string;
|
|
13
|
-
width: number;
|
|
14
|
-
height: number;
|
|
15
|
-
format: string;
|
|
16
|
-
vanityName?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function parseSanityImageAssetId(
|
|
20
|
-
assetId: string
|
|
21
|
-
): SanityImageAssetStub | undefined {
|
|
22
|
-
const matches = assetId.match(/^image-(\w+)-(\d+)x(\d+)-(\w+)$/);
|
|
23
|
-
if (matches) {
|
|
24
|
-
const [, id, width, height, format] = matches;
|
|
25
|
-
return { id, width: Number(width), height: Number(height), format };
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function sanityImageAssetId(source: SanityImageSource): string {
|
|
30
|
-
return typeof source === "string"
|
|
31
|
-
? source
|
|
32
|
-
: isSanityReference(source)
|
|
33
|
-
? source._ref
|
|
34
|
-
: isSanityImageAssetLike(source)
|
|
35
|
-
? source._id
|
|
36
|
-
: sanityImageAssetId(source.asset);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function sanityImageAssetStub(
|
|
40
|
-
source: SanityImageSource
|
|
41
|
-
): SanityImageAssetStub | undefined {
|
|
42
|
-
const id = sanityImageAssetId(source);
|
|
43
|
-
return id ? parseSanityImageAssetId(id) : undefined;
|
|
44
|
-
}
|
package/src/url.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SanityImageParams,
|
|
3
|
-
sanityImageParamsToSearchParamEntries,
|
|
4
|
-
} from "./params";
|
|
5
|
-
import { SanityImageAssetStub } from "./stub";
|
|
6
|
-
|
|
7
|
-
export const defaultSrcsetWidths = [
|
|
8
|
-
6016, // 6K
|
|
9
|
-
5120, // 5K
|
|
10
|
-
4480, // 4.5K
|
|
11
|
-
3840, // 4K
|
|
12
|
-
3200, // QHD+
|
|
13
|
-
2560, // WQXGA
|
|
14
|
-
2048, // QXGA
|
|
15
|
-
1920, // 1080p
|
|
16
|
-
1668, // iPad
|
|
17
|
-
1280, // 720p
|
|
18
|
-
1080, // iPhone 6-8 Plus
|
|
19
|
-
960,
|
|
20
|
-
720, // iPhone 6-8
|
|
21
|
-
640, // 480p
|
|
22
|
-
480,
|
|
23
|
-
360,
|
|
24
|
-
240,
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
export interface SanityClientLike {
|
|
28
|
-
projectId: string;
|
|
29
|
-
dataset: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function sanityImageUrl(
|
|
33
|
-
client: SanityClientLike,
|
|
34
|
-
image: SanityImageAssetStub,
|
|
35
|
-
params?: SanityImageParams
|
|
36
|
-
): string {
|
|
37
|
-
const url = new URL(
|
|
38
|
-
[
|
|
39
|
-
`https://cdn.sanity.io/images`,
|
|
40
|
-
client.projectId,
|
|
41
|
-
client.dataset,
|
|
42
|
-
`${image.id}-${image.width}x${image.height}.${image.format}`,
|
|
43
|
-
image.vanityName,
|
|
44
|
-
]
|
|
45
|
-
.filter(Boolean)
|
|
46
|
-
.join("/")
|
|
47
|
-
);
|
|
48
|
-
if (params) {
|
|
49
|
-
url.search = new URLSearchParams(
|
|
50
|
-
sanityImageParamsToSearchParamEntries(params)
|
|
51
|
-
).toString();
|
|
52
|
-
}
|
|
53
|
-
return url.href;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function sanityImageSrcset(
|
|
57
|
-
client: SanityClientLike,
|
|
58
|
-
image: SanityImageAssetStub,
|
|
59
|
-
params?: Omit<SanityImageParams, "width">,
|
|
60
|
-
widths: number[] = defaultSrcsetWidths
|
|
61
|
-
): string {
|
|
62
|
-
const aspectRatio = image.height / image.width;
|
|
63
|
-
return [
|
|
64
|
-
...widths.sort((a, b) => a - b).filter((width) => width < image.width),
|
|
65
|
-
image.width,
|
|
66
|
-
]
|
|
67
|
-
.map((width) => {
|
|
68
|
-
const url = sanityImageUrl(client, image, {
|
|
69
|
-
...params,
|
|
70
|
-
width,
|
|
71
|
-
height: width * aspectRatio,
|
|
72
|
-
});
|
|
73
|
-
return `${url} ${width}w`;
|
|
74
|
-
})
|
|
75
|
-
.join(",");
|
|
76
|
-
}
|