Skip to content

Commit 33639d4

Browse files
committed
feat(image-loader): integrate query-string for streamlined URL generation
Replaced manual URL parameter handling with `query-string` for improved readability, maintainability, and reduced boilerplate in image loaders. Updated `jest.config.ts` to ensure proper transformation of the `query-string` library, added it as a dependency, and adjusted `pnpm-lock.yaml` accordingly.
1 parent 97a051e commit 33639d4

File tree

9 files changed

+110
-83
lines changed

9 files changed

+110
-83
lines changed

.changeset/cruel-cups-lead.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@codefast/image-loader": patch
3+
"@codefast-ui/checkbox-group": patch
4+
"@codefast/eslint-config": patch
5+
"@codefast/hooks": patch
6+
"@codefast-ui/input": patch
7+
"@codefast-ui/input-number": patch
8+
"@codefast-ui/progress-circle": patch
9+
"@codefast/typescript-config": patch
10+
"@codefast/ui": patch
11+
---
12+
13+
feat(image-loader): integrate `query-string` for streamlined URL generation

packages/image-loader/jest.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ const config: Config = {
3434
/**
3535
* Specifies which files should be ignored during transformation
3636
* Prevents Jest from transforming files in the node_modules directory
37+
* Exception: query-string needs to be transformed as it uses ES modules
3738
*/
38-
transformIgnorePatterns: ["/node_modules/"],
39+
transformIgnorePatterns: ["/node_modules/(?!query-string)"],
3940

4041
verbose: true,
4142

packages/image-loader/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@
5757
"test:watch": "jest --watch",
5858
"type-check": "tsc --noEmit"
5959
},
60+
"dependencies": {
61+
"query-string": "^7.1.3"
62+
},
6063
"devDependencies": {
6164
"@codefast/eslint-config": "workspace:*",
6265
"@codefast/typescript-config": "workspace:*",

packages/image-loader/src/base-loader.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { ImageLoaderProps } from "next/image";
2+
import queryString from "query-string";
23

34
import type { ImageLoader } from "@/types";
45

@@ -91,20 +92,12 @@ export abstract class BaseImageLoader implements ImageLoader {
9192
}
9293

9394
/**
94-
* Utility method to build query parameters
95+
* Utility method to build query parameters using query-string library
9596
* Helper method for URL construction
9697
*/
9798
protected buildQueryParams(params: Record<string, number | string | undefined>): string {
98-
const searchParams = new URLSearchParams();
99-
100-
for (const [key, value] of Object.entries(params)) {
101-
if (value !== undefined) {
102-
searchParams.append(key, String(value));
103-
}
104-
}
105-
106-
const queryString = searchParams.toString();
107-
return queryString ? `?${queryString}` : "";
99+
const queryString_ = queryString.stringify(params);
100+
return queryString_ ? `?${queryString_}` : "";
108101
}
109102

110103
/**

packages/image-loader/src/loaders/aws-cloudfront-loader.ts

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import type { ImageLoaderProps } from "next/image";
2+
import type { StringifiableRecord } from "query-string";
3+
import queryString from "query-string";
24

35
import { BaseImageLoader } from "@/base-loader";
46

@@ -23,30 +25,24 @@ export class AWSCloudFrontLoader extends BaseImageLoader {
2325
const { quality, src, width } = config;
2426

2527
try {
26-
const url = new URL(src);
27-
2828
// AWS CloudFront with Lambda@Edge or CloudFront Functions
2929
// Common query parameters for image optimization:
3030
// w = width
3131
// q = quality
3232
// f = format
3333
// fit = fit mode
3434

35-
// Set width parameter
36-
url.searchParams.set("w", String(width));
37-
38-
// Set quality parameter
39-
if (quality !== undefined) {
40-
url.searchParams.set("q", String(quality));
41-
}
42-
43-
// Request auto format (if supported by the Lambda function)
44-
url.searchParams.set("f", "auto");
45-
46-
// Use cover fit mode for consistent sizing
47-
url.searchParams.set("fit", "cover");
48-
49-
return url.toString();
35+
const params: StringifiableRecord = {
36+
f: "auto",
37+
fit: "cover",
38+
q: quality,
39+
w: width,
40+
};
41+
42+
return queryString.stringifyUrl({
43+
query: params,
44+
url: src,
45+
});
5046
} catch (error) {
5147
console.warn(`Failed to transform AWS CloudFront URL: ${src}`, error);
5248
return src;

packages/image-loader/src/loaders/imgix-loader.ts

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { ImageLoaderProps } from "next/image";
2+
import queryString, { type StringifiableRecord } from "query-string";
23

34
import { BaseImageLoader } from "@/base-loader";
45

@@ -23,33 +24,25 @@ export class ImgixLoader extends BaseImageLoader {
2324
const { quality, src, width } = config;
2425

2526
try {
26-
const url = new URL(src);
27-
2827
// Imgix URL parameters:
2928
// w = width
3029
// q = quality (1-100)
3130
// auto = auto format and compress
3231
// fit = fit mode (crop, scale, etc.)
3332
// crop = crop mode (faces, entropy, etc.)
3433

35-
// Set width parameter
36-
url.searchParams.set("w", String(width));
37-
38-
// Set quality parameter
39-
if (quality !== undefined) {
40-
url.searchParams.set("q", String(quality));
41-
}
42-
43-
// Enable auto format and compression
44-
url.searchParams.set("auto", "format,compress");
45-
46-
// Use crop fit mode for consistent sizing
47-
url.searchParams.set("fit", "crop");
48-
49-
// Use faces crop mode for better cropping of images with people
50-
url.searchParams.set("crop", "faces,entropy");
51-
52-
return url.toString();
34+
const params: StringifiableRecord = {
35+
auto: "format,compress",
36+
crop: "faces,entropy",
37+
fit: "crop",
38+
q: quality,
39+
w: width,
40+
};
41+
42+
return queryString.stringifyUrl({
43+
query: params,
44+
url: src,
45+
});
5346
} catch (error) {
5447
console.warn(`Failed to transform Imgix URL: ${src}`, error);
5548
return src;

packages/image-loader/src/loaders/supabase-loader.ts

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { ImageLoaderProps } from "next/image";
2+
import queryString, { type StringifiableRecord } from "query-string";
23

34
import { BaseImageLoader } from "@/base-loader";
45

@@ -23,29 +24,23 @@ export class SupabaseLoader extends BaseImageLoader {
2324
const { quality, src, width } = config;
2425

2526
try {
26-
const url = new URL(src);
27-
2827
// Supabase Storage image transformation parameters:
2928
// width = width
3029
// quality = quality (1-100)
3130
// format = format (auto, webp, jpg, png)
3231
// resize = resize mode (cover, contain, fill)
3332

34-
// Set width parameter
35-
url.searchParams.set("width", String(width));
36-
37-
// Set quality parameter
38-
if (quality !== undefined) {
39-
url.searchParams.set("quality", String(quality));
40-
}
41-
42-
// Request auto format for best optimization
43-
url.searchParams.set("format", "auto");
44-
45-
// Use cover resize mode for consistent sizing
46-
url.searchParams.set("resize", "cover");
47-
48-
return url.toString();
33+
const params: StringifiableRecord = {
34+
format: "auto",
35+
quality: quality,
36+
resize: "cover",
37+
width: width,
38+
};
39+
40+
return queryString.stringifyUrl({
41+
query: params,
42+
url: src,
43+
});
4944
} catch (error) {
5045
console.warn(`Failed to transform Supabase URL: ${src}`, error);
5146
return src;

packages/image-loader/src/loaders/unsplash-loader.ts

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { ImageLoaderProps } from "next/image";
2+
import queryString, { type StringifiableRecord } from "query-string";
23

34
import { BaseImageLoader } from "@/base-loader";
45

@@ -23,29 +24,23 @@ export class UnsplashLoader extends BaseImageLoader {
2324
const { quality, src, width } = config;
2425

2526
try {
26-
const url = new URL(src);
27-
2827
// Unsplash URL parameters:
2928
// w = width
3029
// q = quality (1-100)
3130
// fm = format (auto, jpg, png, webp)
3231
// fit = fit mode (crop, scale, etc.)
3332

34-
// Set width parameter
35-
url.searchParams.set("w", String(width));
36-
37-
// Set quality parameter
38-
if (quality !== undefined) {
39-
url.searchParams.set("q", String(quality));
40-
}
41-
42-
// Set format to auto for the best optimization
43-
url.searchParams.set("fm", "auto");
44-
45-
// Use crop fit mode for consistent sizing
46-
url.searchParams.set("fit", "crop");
47-
48-
return url.toString();
33+
const params: StringifiableRecord = {
34+
fit: "crop",
35+
fm: "auto",
36+
q: quality,
37+
w: width,
38+
};
39+
40+
return queryString.stringifyUrl({
41+
query: params,
42+
url: src,
43+
});
4944
} catch (error) {
5045
console.warn(`Failed to transform Unsplash URL: ${src}`, error);
5146
return src;

pnpm-lock.yaml

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)