Skip to content

Commit 3d79060

Browse files
authored
fix!: use URLSearchParams as default formatter (#1813)
1 parent 3041371 commit 3d79060

33 files changed

+76
-87
lines changed

src/runtime/providers/aliyun.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import { joinURL } from 'ufo'
1+
import { joinURL, encodePath } from 'ufo'
22
import { defineProvider, createOperationsGenerator } from '#image'
33

44
const operationsGenerator = createOperationsGenerator({
55
joinWith: '/',
66
formatter: (key: string | number, value: string | number | ReturnType<typeof getResizeValue>) => {
77
if (typeof value === 'object') {
8-
return `${key},${Object.entries(value).map(([k, v]) => `${k}_${v}`).join(',')}`
8+
return `${key},${encodePath(Object.entries(value).map(([k, v]) => `${k}_${v}`).join(','))}`
99
}
10-
return `${key},${value}`
10+
return encodePath(`${key},${value}`)
1111
},
1212
})
1313

src/runtime/providers/awsAmplify.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ const operationsGenerator = createOperationsGenerator({
4545
left: 'left',
4646
},
4747
},
48-
joinWith: '&',
49-
formatter: (key, value) => `${key}=${value}`,
5048
})
5149

5250
interface AmplifyOptions {

src/runtime/providers/bunny.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ const operationsGenerator = createOperationsGenerator({
2020
autoOptimize: 'auto_optimize',
2121
sepia: 'sepia',
2222
},
23-
joinWith: '&',
24-
formatter: (key, value) => `${key}=${value}`,
2523
})
2624

2725
interface BunnyOptions {

src/runtime/providers/caisy.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ const operationsGenerator = createOperationsGenerator({
77
height: 'h',
88
quality: 'q',
99
},
10-
joinWith: '&',
11-
formatter: (key, value) => `${key}=${value}`,
1210
})
1311

1412
export default defineProvider({

src/runtime/providers/cloudflare.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { joinURL, encodeQueryItem } from 'ufo'
1+
import { encodeQueryItem, joinURL } from 'ufo'
22
import { defineProvider, createOperationsGenerator } from '#image'
33

44
const operationsGenerator = createOperationsGenerator({
@@ -26,7 +26,7 @@ const operationsGenerator = createOperationsGenerator({
2626
},
2727
},
2828
joinWith: ',',
29-
formatter: (key, val) => encodeQueryItem(key, val),
29+
formatter: (key, value) => encodeQueryItem(key, value),
3030
})
3131

3232
const defaultModifiers = {}

src/runtime/providers/cloudimage.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ const operationsGenerator = createOperationsGenerator({
1616
outside: 'boundmin',
1717
},
1818
},
19-
joinWith: '&',
20-
formatter: (key, value) => `${key}=${value}`,
2119
})
2220

2321
interface CloudimageOptions {

src/runtime/providers/cloudinary.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ const operationsGenerator = createOperationsGenerator({
103103
},
104104
},
105105
joinWith: ',',
106-
formatter: (key, value) => key.includes('_') ? `${key}:${value}` : `${key}_${value}`,
106+
formatter: (key, value) => encodePath(key.includes('_') ? `${key}:${value}` : `${key}_${value}`),
107107
})
108108

109109
const defaultModifiers = {

src/runtime/providers/contentful.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ const operationsGenerator = createOperationsGenerator({
2525
thumbnail: 'thumb',
2626
},
2727
},
28-
joinWith: '&',
29-
formatter: (key, value) => `${key}=${value}`,
3028
})
3129

3230
interface ContentfulOptions {

src/runtime/providers/directus.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { joinURL } from 'ufo'
22
import { defineProvider, createOperationsGenerator } from '#image'
33

4-
const operationsGenerator = createOperationsGenerator({
5-
joinWith: '&',
6-
})
4+
const operationsGenerator = createOperationsGenerator()
75

86
interface DirectusOptions {
97
baseURL: string
@@ -16,15 +14,15 @@ export default defineProvider<DirectusOptions>({
1614
getImage: (src, { modifiers, baseURL }) => {
1715
// Separating the transforms from the rest of the modifiers
1816
const transforms = modifiers.transforms && Array.isArray(modifiers.transforms) && modifiers.transforms.length > 0
19-
? new URLSearchParams(JSON.stringify(Array.from(new Set(modifiers.transforms.map(obj => JSON.stringify(obj)))).map(value => JSON.parse(value)))).toString().replace(/=+$/, '')
17+
? JSON.stringify(Array.from(new Set(modifiers.transforms.map(obj => JSON.stringify(obj)))).map(value => JSON.parse(value)))
2018
: undefined
2119

2220
const operations = operationsGenerator({
2321
...modifiers,
2422
transforms,
2523
})
2624
return {
27-
url: joinURL(baseURL, src + (operations ? ('?' + operations) : '')),
25+
url: joinURL(baseURL, src + (operations ? ('?' + operations.replace(/=+$/, '')) : '')),
2826
}
2927
},
3028
})

src/runtime/providers/fastly.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ const operationsGenerator = createOperationsGenerator({
1111
contain: 'bounds',
1212
},
1313
},
14-
joinWith: '&',
15-
formatter: (key, value) => `${key}=${value}`,
1614
})
1715

1816
interface FastlyOptions {

src/runtime/providers/filerobot.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ const operationsGenerator = createOperationsGenerator({
1818
outside: 'boundmin',
1919
},
2020
},
21-
joinWith: '&',
22-
formatter: (key, value) => `${key}=${value}`,
2321
})
2422

2523
interface FilerobotOptions {

src/runtime/providers/glide.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// https://glide.thephpleague.com/2.0/api/quick-reference/
22

3-
import { joinURL, encodeQueryItem, encodePath, withBase } from 'ufo'
3+
import { joinURL, encodePath, withBase } from 'ufo'
44
import { defineProvider, createOperationsGenerator } from '#image'
55

66
const operationsGenerator = createOperationsGenerator({
@@ -41,8 +41,6 @@ const operationsGenerator = createOperationsGenerator({
4141
contain: 'contain',
4242
},
4343
},
44-
joinWith: '&',
45-
formatter: (key, val) => encodeQueryItem(key, val),
4644
})
4745

4846
interface GlideOptions {

src/runtime/providers/gumlet.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,6 @@ const operationsGenerator = createOperationsGenerator({
6565
auto: 'auto',
6666
},
6767
},
68-
joinWith: '&',
69-
formatter: (key, value) => `${key}=${value}`,
7068
})
7169

7270
interface GumletOptions {

src/runtime/providers/imageengine.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { joinURL } from 'ufo'
1+
import { joinURL, encodePath } from 'ufo'
22
import { defineProvider, createOperationsGenerator } from '#image'
33

44
const operationsGenerator = createOperationsGenerator({
@@ -44,7 +44,7 @@ const operationsGenerator = createOperationsGenerator({
4444
},
4545
},
4646
joinWith: '/',
47-
formatter: (key, value: string | number) => `${key}_${value}`,
47+
formatter: (key, value: string | number) => encodePath(`${key}_${value}`),
4848
})
4949

5050
interface ImageEngineOptions {

src/runtime/providers/imagekit.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { joinURL, withQuery } from 'ufo'
1+
import { joinURL, withQuery, encodePath } from 'ufo'
22
import { defineProvider, createOperationsGenerator } from '#image'
33

44
const operationsGenerator = createOperationsGenerator({
@@ -99,7 +99,7 @@ const operationsGenerator = createOperationsGenerator({
9999
},
100100
},
101101
joinWith: ',',
102-
formatter: (key, value: string | number) => `${key}-${value}`,
102+
formatter: (key, value: string | number) => encodePath(`${key}-${value}`),
103103
})
104104

105105
interface ImagekitOptions {

src/runtime/providers/imgix.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,6 @@ export const operationsGenerator = createOperationsGenerator({
159159
blurhash: 'blurhash',
160160
},
161161
},
162-
joinWith: '&',
163-
formatter: (key, value) => `${key}=${value}`,
164162
})
165163

166164
interface ImgixOptions {

src/runtime/providers/ipx.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ export const operationsGenerator = createOperationsGenerator({
4848
quality: 'q',
4949
background: 'b',
5050
},
51-
joinWith: '&',
5251
formatter: (key, val: string | number | boolean) => encodeParam(key) + '_' + encodeParam(val.toString()),
5352
})
5453

src/runtime/providers/netlifyImageCdn.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { encodeQueryItem } from 'ufo'
21
import { defineProvider, createOperationsGenerator } from '#image'
32

43
// https://docs.netlify.com/image-cdn/overview/
@@ -33,8 +32,6 @@ const operationsGenerator = createOperationsGenerator({
3332
center: 'center',
3433
},
3534
},
36-
joinWith: '&',
37-
formatter: (key, value) => encodeQueryItem(key, value),
3835
})
3936

4037
interface NetlifyImageCDNOptions {

src/runtime/providers/netlifyLargeMedia.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { encodeQueryItem, joinURL } from 'ufo'
1+
import { joinURL } from 'ufo'
22
import { defineProvider, createOperationsGenerator } from '#image'
33

44
const operationsGenerator = createOperationsGenerator({
@@ -13,8 +13,6 @@ const operationsGenerator = createOperationsGenerator({
1313
contain: 'fit',
1414
},
1515
},
16-
joinWith: '&',
17-
formatter: (key, value) => encodeQueryItem(key, value),
1816
})
1917

2018
interface NetlifyLargeMediaOptions {

src/runtime/providers/sanity.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { joinURL } from 'ufo'
1+
import { joinURL, encodePath } from 'ufo'
22
import { defineProvider, createOperationsGenerator } from '#image'
33

44
const sanityCDN = 'https://cdn.sanity.io/images'
@@ -37,8 +37,7 @@ const operationsGenerator = createOperationsGenerator({
3737
outside: 'max',
3838
},
3939
},
40-
joinWith: '&',
41-
formatter: (key, value: string | true | number | Record<string, unknown>) => String(value) === 'true' ? key : `${key}=${value}`,
40+
formatter: (key, value: string | true | number | Record<string, unknown>) => String(value) === 'true' ? key : encodePath(`${key}=${value}`),
4241
})
4342

4443
const getMetadata = (id: string) => {

src/runtime/providers/sirv.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,6 @@ const operationsGenerator = createOperationsGenerator({
9898
},
9999
...generateColorKeys(),
100100
},
101-
joinWith: '&',
102-
formatter: (key: any, value: any) => `${key}=${value}`,
103101
})
104102

105103
interface SirvOptions {

src/runtime/providers/twicpics.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { joinURL } from 'ufo'
1+
import { encodeQueryItem, joinURL } from 'ufo'
22
import { defineProvider } from '#image'
33
import { createMapper, createOperationsGenerator, type InferModifiers } from '#image'
44

@@ -47,13 +47,14 @@ const operationsGenerator = createOperationsGenerator({
4747
},
4848
},
4949
joinWith: '/',
50-
formatter: (key, value: string | number) => `${key}=${value}`,
51-
})
50+
formatter: (key: 'output' | 'format' | 'fit' | 'quality' | 'background' | 'focus' | 'zoom', value) => encodeQueryItem(key, value),
51+
} as const)
5252

5353
interface TwicpicsOptions {
5454
baseURL?: string
5555
modifiers?: InferModifiers<typeof operationsGenerator>
5656
& { fit?: 'fill' | 'inside' | 'outside' | 'cover' | 'contain' }
57+
& Partial<Record<'resize' | 'fill' | 'contain' | 'inside' | 'outside' | 'cover' | 'missingValue', string>>
5758
& Partial<Record<typeof fits extends (fit: string) => infer Fit ? NonNullable<Fit> : string, string>>
5859
}
5960

src/runtime/providers/weserv.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,6 @@ const operationsGenerator = createOperationsGenerator({
5858
'heart': 'heart',
5959
},
6060
},
61-
joinWith: '&',
62-
formatter: (key, value) => `${key}=${value}`,
6361
})
6462

6563
export interface WeservOptions {

src/runtime/utils/index.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,8 @@ export function createMapper<Key extends string, Value>(map: Partial<Record<Key,
3232
return (key => key !== undefined ? map[key as Extract<Key, string>] || key : map.missingValue) as Mapper<Key, Value>
3333
}
3434

35-
type Formatter<Key, Value> = (key: Key, value: Value) => string
36-
37-
const defaultFormatter = (key: string, value: string | number) => `${key}=${value}`
38-
39-
export function createOperationsGenerator<ModifierKey extends string, ModifierValue = string | number, FinalKey = ModifierKey, FinalValue = ModifierValue>(config: OperationGeneratorConfig<ModifierKey, ModifierValue, FinalKey, FinalValue>) {
40-
const formatter = config.formatter || (defaultFormatter as Formatter<FinalKey, FinalValue>)
41-
35+
export function createOperationsGenerator<ModifierKey extends string, ModifierValue = string | number, FinalKey = ModifierKey, FinalValue = ModifierValue>(config: OperationGeneratorConfig<ModifierKey, ModifierValue, FinalKey, FinalValue> = {}) {
36+
const formatter = config.formatter
4237
const keyMap = config.keyMap && typeof config.keyMap !== 'function' ? createMapper<ModifierKey, FinalKey>(config.keyMap) : config.keyMap
4338

4439
const map: Record<string, Mapper<ModifierValue, FinalValue>> = {}
@@ -51,7 +46,7 @@ export function createOperationsGenerator<ModifierKey extends string, ModifierVa
5146
}
5247

5348
return (modifiers: Partial<Record<Extract<ModifierKey | FinalKey, string>, ModifierValue | FinalValue>>): string => {
54-
const operations: string[] = []
49+
const operations: [key: FinalKey, value: FinalValue][] = []
5550
for (const _key in modifiers) {
5651
const key = _key as keyof typeof modifiers
5752
if (typeof modifiers[key] === 'undefined') {
@@ -61,10 +56,14 @@ export function createOperationsGenerator<ModifierKey extends string, ModifierVa
6156
? map[key](modifiers[key] as ModifierValue)
6257
: modifiers[key]
6358

64-
operations.push(formatter(((keyMap ? keyMap(key as ModifierKey) : key) as FinalKey), value as FinalValue))
59+
operations.push([(keyMap ? keyMap(key as ModifierKey) : key) as FinalKey, value as FinalValue])
60+
}
61+
62+
if (formatter) {
63+
return operations.map(entry => formatter(...entry)).join(config.joinWith ?? '&')
6564
}
6665

67-
return operations.join(config.joinWith ?? '/')
66+
return new URLSearchParams(operations as [string, string][]).toString()
6867
}
6968
}
7069

src/types/image.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,16 @@ export interface ImageCTX {
9393

9494
export type OperationMapper<From, To> = Record<string | Extract<From, string | number>, To> | ((key?: From) => To | From | undefined)
9595

96-
export interface OperationGeneratorConfig<Key extends string, Value, FinalKey, FinalValue> {
96+
export type OperationGeneratorConfig<Key extends string, Value, FinalKey, FinalValue> = {
9797
keyMap?: Partial<Record<Key, FinalKey>>
98+
valueMap?: Partial<Record<Key, Partial<Record<Extract<Value, string>, FinalValue>> | ((key: Value) => Value | FinalValue)>>
99+
} & ({
98100
formatter?: (key: FinalKey, value: FinalValue) => string
99-
joinWith?: string
100-
valueMap?: Partial<Record<Key, Record<Extract<Value, string>, FinalValue> | ((key: Value) => Value | FinalValue)>>
101-
}
101+
joinWith?: undefined
102+
} | {
103+
formatter: (key: FinalKey, value: FinalValue) => string
104+
joinWith: string
105+
})
102106

103107
export interface ImageSizesVariant {
104108
size?: string

test/e2e/__snapshots__/bunny.json5

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"requests": [
3-
"https://bunnyoptimizerdemo.b-cdn.net/bunny1.jpg?sharpen=true&aspect_ratio=16:9&width=1000&quality=80",
4-
"https://bunnyoptimizerdemo.b-cdn.net/bunny2.jpg?crop=750,750&crop_gravity=north",
3+
"https://bunnyoptimizerdemo.b-cdn.net/bunny1.jpg?sharpen=true&aspect_ratio=16%3A9&width=1000&quality=80",
4+
"https://bunnyoptimizerdemo.b-cdn.net/bunny2.jpg?crop=750%2C750&crop_gravity=north",
55
"https://bunnyoptimizerdemo.b-cdn.net/bunny3.jpg?flop=true&auto_optimize=high",
66
],
77
"sources": [
8-
"https://bunnyoptimizerdemo.b-cdn.net/bunny1.jpg?sharpen=true&aspect_ratio=16:9&width=1000&quality=80",
9-
"https://bunnyoptimizerdemo.b-cdn.net/bunny2.jpg?crop=750,750&crop_gravity=north",
8+
"https://bunnyoptimizerdemo.b-cdn.net/bunny1.jpg?sharpen=true&aspect_ratio=16%3A9&width=1000&quality=80",
9+
"https://bunnyoptimizerdemo.b-cdn.net/bunny2.jpg?crop=750%2C750&crop_gravity=north",
1010
"https://bunnyoptimizerdemo.b-cdn.net/bunny3.jpg?flop=true&auto_optimize=high",
1111
],
1212
}

test/e2e/__snapshots__/sirv.json5

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"https://demo.sirv.com/look-big.jpg?crop.type=face&h=500",
77
"https://demo.sirv.com/look-big.jpg?h=500&scale.option=fit",
88
"https://demo.sirv.com/look-big.jpg?text=Hello&text.align=center&text.position.gravity=south&text.background.color=ffff&text.size=60&text.font.family=Arial&text.color=white&h=500",
9-
"https://demo.sirv.com/t-shirt-man.jpg?watermark=/watermark-v1.png&watermark.position=center&watermark.scale.width=30%&h=500",
9+
"https://demo.sirv.com/t-shirt-man.jpg?watermark=%2Fwatermark-v1.png&watermark.position=center&watermark.scale.width=30%25&h=500",
1010
"https://demo.sirv.com/test.png?w=750&q=75",
1111
],
1212
"sources": [
@@ -17,6 +17,6 @@
1717
"https://demo.sirv.com/look-big.jpg?crop.type=face&h=500",
1818
"https://demo.sirv.com/QW.pdf?page=1&w=500",
1919
"https://demo.sirv.com/look-big.jpg?text=Hello&text.align=center&text.position.gravity=south&text.background.color=ffff&text.size=60&text.font.family=Arial&text.color=white&h=500",
20-
"https://demo.sirv.com/t-shirt-man.jpg?watermark=/watermark-v1.png&watermark.position=center&watermark.scale.width=30%&h=500",
20+
"https://demo.sirv.com/t-shirt-man.jpg?watermark=%2Fwatermark-v1.png&watermark.position=center&watermark.scale.width=30%25&h=500",
2121
],
2222
}

0 commit comments

Comments
 (0)