From 3f5ce73016ab8cb3d6b677ab6f44fd781672cd61 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 5 May 2025 15:39:49 +0530 Subject: [PATCH 01/10] feat: add getImageProps function for responsive image handling --- src/getImageProps.ts | 102 +++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 3 +- 2 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 src/getImageProps.ts diff --git a/src/getImageProps.ts b/src/getImageProps.ts new file mode 100644 index 0000000..4903ba2 --- /dev/null +++ b/src/getImageProps.ts @@ -0,0 +1,102 @@ +import { buildSrc } from './url' +import type { SrcOptions } from './interfaces' + +const DEVICE_SIZES = [640, 750, 828, 1080, 1200, 1920, 2048, 3840] as const +const IMAGE_SIZES = [16, 32, 48, 64, 96, 128, 256, 320, 420] as const + +export interface ResponsiveOptions extends SrcOptions { + width?: number + sizes?: string + deviceSizes?: number[] + imageSizes?: number[] +} + +export interface ImageProps { + src: string + srcSet?: string + sizes?: string + width?: number + height?: number +} + +export function getImageProps(opts: ResponsiveOptions): ImageProps { + const { + src, + urlEndpoint, + transformation = [], + queryParameters, + transformationPosition, + sizes, + width, + deviceSizes = DEVICE_SIZES as unknown as number[], + imageSizes = IMAGE_SIZES as unknown as number[], + } = opts + + const allBreakpoints = [...imageSizes, ...deviceSizes].sort((a, b) => a - b) + + const { widths, kind } = pickWidths({ + all: allBreakpoints, + device: deviceSizes, + explicit: width, + sizesAttr: sizes, + }) + + const build = (w: number) => + buildSrc({ + src, + urlEndpoint, + queryParameters, + transformationPosition, + transformation: [ + ...transformation, + { width: w, crop: "at_max" } // Should never upscale beyond the original width + ], + }) + + const srcSet = + widths + .map((w, i) => `${build(w)} ${kind === 'w' ? w : i + 1}${kind}`) + .join(', ') || undefined + + return { + sizes: sizes ?? (kind === 'w' ? '100vw' : undefined), + srcSet, + src: build(widths[widths.length - 1]), + width, + } +} + +function pickWidths({ + all, + device, + explicit, + sizesAttr, +}: { + all: number[] + device: number[] + explicit?: number + sizesAttr?: string +}): { widths: number[]; kind: 'w' | 'x' } { + if (sizesAttr) { + const vwMatches = sizesAttr.match(/(^|\s)(1?\d{1,2})vw/g) || [] + const percents = vwMatches.map((m) => parseInt(m, 10)) + + if (percents.length) { + const smallest = Math.min(...percents) / 100 + const cutOff = device[0] * smallest + return { widths: all.filter((w) => w >= cutOff), kind: 'w' } + } + + return { widths: all, kind: 'w' } // ← return allSizes when no vw tokens + } + + // If sizes is not defined, we need to check if the explicit width is defined. If no width is defined, we can use the deviceSizes as the default. + if (typeof explicit !== 'number') { + return { widths: device, kind: 'w' } + } + + const nearest = (t: number) => + all.find((v) => v >= t) || all[all.length - 1] + const list = Array.from(new Set([nearest(explicit), nearest(explicit * 2)])) + return { widths: list, kind: 'x' } +} diff --git a/src/index.ts b/src/index.ts index 4b047e1..46bd971 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,9 @@ import type { SrcOptions, Transformation, UploadOptions, UploadResponse } from "./interfaces"; import { ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, ImageKitUploadNetworkError, upload } from "./upload"; import { buildSrc, buildTransformationString } from "./url"; +import { getImageProps } from "./getImageProps"; -export { buildSrc, buildTransformationString, upload, ImageKitInvalidRequestError, ImageKitAbortError, ImageKitServerError, ImageKitUploadNetworkError }; +export { buildSrc, buildTransformationString, upload, getImageProps, ImageKitInvalidRequestError, ImageKitAbortError, ImageKitServerError, ImageKitUploadNetworkError }; export type { Transformation, SrcOptions, From 83d38b12936e1764d546a5c85d9f7e902d228546 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 5 May 2025 16:05:29 +0530 Subject: [PATCH 02/10] feat: implement getResponsiveImageAttributes function and corresponding tests --- src/getImageProps.ts | 102 ----------------- src/getResponsiveImageAttributes.ts | 112 +++++++++++++++++++ src/index.ts | 6 +- test/getResponsiveImageAttributes.test.js | 130 ++++++++++++++++++++++ 4 files changed, 244 insertions(+), 106 deletions(-) delete mode 100644 src/getImageProps.ts create mode 100644 src/getResponsiveImageAttributes.ts create mode 100644 test/getResponsiveImageAttributes.test.js diff --git a/src/getImageProps.ts b/src/getImageProps.ts deleted file mode 100644 index 4903ba2..0000000 --- a/src/getImageProps.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { buildSrc } from './url' -import type { SrcOptions } from './interfaces' - -const DEVICE_SIZES = [640, 750, 828, 1080, 1200, 1920, 2048, 3840] as const -const IMAGE_SIZES = [16, 32, 48, 64, 96, 128, 256, 320, 420] as const - -export interface ResponsiveOptions extends SrcOptions { - width?: number - sizes?: string - deviceSizes?: number[] - imageSizes?: number[] -} - -export interface ImageProps { - src: string - srcSet?: string - sizes?: string - width?: number - height?: number -} - -export function getImageProps(opts: ResponsiveOptions): ImageProps { - const { - src, - urlEndpoint, - transformation = [], - queryParameters, - transformationPosition, - sizes, - width, - deviceSizes = DEVICE_SIZES as unknown as number[], - imageSizes = IMAGE_SIZES as unknown as number[], - } = opts - - const allBreakpoints = [...imageSizes, ...deviceSizes].sort((a, b) => a - b) - - const { widths, kind } = pickWidths({ - all: allBreakpoints, - device: deviceSizes, - explicit: width, - sizesAttr: sizes, - }) - - const build = (w: number) => - buildSrc({ - src, - urlEndpoint, - queryParameters, - transformationPosition, - transformation: [ - ...transformation, - { width: w, crop: "at_max" } // Should never upscale beyond the original width - ], - }) - - const srcSet = - widths - .map((w, i) => `${build(w)} ${kind === 'w' ? w : i + 1}${kind}`) - .join(', ') || undefined - - return { - sizes: sizes ?? (kind === 'w' ? '100vw' : undefined), - srcSet, - src: build(widths[widths.length - 1]), - width, - } -} - -function pickWidths({ - all, - device, - explicit, - sizesAttr, -}: { - all: number[] - device: number[] - explicit?: number - sizesAttr?: string -}): { widths: number[]; kind: 'w' | 'x' } { - if (sizesAttr) { - const vwMatches = sizesAttr.match(/(^|\s)(1?\d{1,2})vw/g) || [] - const percents = vwMatches.map((m) => parseInt(m, 10)) - - if (percents.length) { - const smallest = Math.min(...percents) / 100 - const cutOff = device[0] * smallest - return { widths: all.filter((w) => w >= cutOff), kind: 'w' } - } - - return { widths: all, kind: 'w' } // ← return allSizes when no vw tokens - } - - // If sizes is not defined, we need to check if the explicit width is defined. If no width is defined, we can use the deviceSizes as the default. - if (typeof explicit !== 'number') { - return { widths: device, kind: 'w' } - } - - const nearest = (t: number) => - all.find((v) => v >= t) || all[all.length - 1] - const list = Array.from(new Set([nearest(explicit), nearest(explicit * 2)])) - return { widths: list, kind: 'x' } -} diff --git a/src/getResponsiveImageAttributes.ts b/src/getResponsiveImageAttributes.ts new file mode 100644 index 0000000..3752121 --- /dev/null +++ b/src/getResponsiveImageAttributes.ts @@ -0,0 +1,112 @@ +import type { SrcOptions } from './interfaces' +import { buildSrc } from './url' + +/* Default break‑point pools */ +const DEFAULT_DEVICE_BREAKPOINTS = [640, 750, 828, 1080, 1200, 1920, 2048, 3840] as const +const DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 320, 420] as const + +export interface GetImageAttributesOptions extends SrcOptions { + width?: number // explicit rendered width + sizes?: string // the HTML sizes value + deviceBreakpoints?: number[] // override default device break‑points + imageBreakpoints?: number[] // override tiny image break‑points +} + +export interface ResponsiveImageAttributes { + src: string + srcSet?: string + sizes?: string + width?: number +} + +export function getResponsiveImageAttributes( + opts: GetImageAttributesOptions +): ResponsiveImageAttributes { + const { + src, + urlEndpoint, + transformation = [], + queryParameters, + transformationPosition, + sizes, + width, + deviceBreakpoints = DEFAULT_DEVICE_BREAKPOINTS as unknown as number[], + imageBreakpoints = DEFAULT_IMAGE_BREAKPOINTS as unknown as number[], + } = opts + + const allBreakpoints = [...imageBreakpoints, ...deviceBreakpoints].sort((a, b) => a - b) + + const { candidates, descriptorKind } = computeCandidateWidths({ + allBreakpoints, + deviceBreakpoints, + explicitWidth: width, + sizesAttr: sizes, + }) + + /* helper to build a single ImageKit URL */ + const buildURL = (w: number) => + buildSrc({ + src, + urlEndpoint, + queryParameters, + transformationPosition, + transformation: [ + ...transformation, + { width: w, crop: 'at_max' }, // never upscale beyond original + ], + }) + + /* build srcSet */ + const srcSet = + candidates + .map((w, i) => `${buildURL(w)} ${descriptorKind === 'w' ? w : i + 1}${descriptorKind}`) + .join(', ') || undefined + + return { + src: buildURL(candidates[candidates.length - 1]), // largest candidate + srcSet, + sizes: sizes ?? (descriptorKind === 'w' ? '100vw' : undefined), + width, + } +} + +function computeCandidateWidths(params: { + allBreakpoints: number[] + deviceBreakpoints: number[] + explicitWidth?: number + sizesAttr?: string +}): { candidates: number[]; descriptorKind: 'w' | 'x' } { + const { allBreakpoints, deviceBreakpoints, explicitWidth, sizesAttr } = params + + /* --- sizes attribute present ----------------------------------- */ + if (sizesAttr) { + const vwTokens = sizesAttr.match(/(^|\s)(1?\d{1,2})vw/g) || [] + const vwPercents = vwTokens.map((t) => parseInt(t, 10)) + + if (vwPercents.length) { + const smallestRatio = Math.min(...vwPercents) / 100 + const minRequiredPx = deviceBreakpoints[0] * smallestRatio + return { + candidates: allBreakpoints.filter((w) => w >= minRequiredPx), + descriptorKind: 'w', + } + } + /* no vw → give the full break‑point list */ + return { candidates: allBreakpoints, descriptorKind: 'w' } + } + + /* --- no sizes attr ------------------------------------------------ */ + if (typeof explicitWidth !== 'number') { + return { candidates: deviceBreakpoints, descriptorKind: 'w' } + } + + /* DPR strategy: 1× & 2× nearest break‑points */ + const nearest = (t: number) => + allBreakpoints.find((n) => n >= t) || allBreakpoints[allBreakpoints.length - 1] + + const unique = Array.from( + new Set([nearest(explicitWidth), nearest(explicitWidth * 2)]), + ) + + return { candidates: unique, descriptorKind: 'x' } +} diff --git a/src/index.ts b/src/index.ts index 46bd971..321c824 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,14 +1,12 @@ import type { SrcOptions, Transformation, UploadOptions, UploadResponse } from "./interfaces"; import { ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, ImageKitUploadNetworkError, upload } from "./upload"; import { buildSrc, buildTransformationString } from "./url"; -import { getImageProps } from "./getImageProps"; +import { getResponsiveImageAttributes } from "./getResponsiveImageAttributes"; -export { buildSrc, buildTransformationString, upload, getImageProps, ImageKitInvalidRequestError, ImageKitAbortError, ImageKitServerError, ImageKitUploadNetworkError }; +export { buildSrc, buildTransformationString, upload, getResponsiveImageAttributes, ImageKitInvalidRequestError, ImageKitAbortError, ImageKitServerError, ImageKitUploadNetworkError }; export type { Transformation, SrcOptions, UploadOptions, UploadResponse }; - - diff --git a/test/getResponsiveImageAttributes.test.js b/test/getResponsiveImageAttributes.test.js new file mode 100644 index 0000000..b6a4db8 --- /dev/null +++ b/test/getResponsiveImageAttributes.test.js @@ -0,0 +1,130 @@ +const { expect } = require('chai'); +const { getResponsiveImageAttributes } = require('../src/getResponsiveImageAttributes'); + +describe.only('getResponsiveImageAttributes – smoke run‑through', () => { + it('bare minimum input', () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + }); + // Expected object based on default deviceSizes and imageSizes: + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", + sizes: "100vw", + width: undefined + }); + }); + + it('sizes provided (100vw)', () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + sizes: '100vw', + }); + // With a sizes value of "100vw", the function should use the same breakpoints as in the bare minimum case. + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", + sizes: "100vw", + width: undefined + }); + }); + + it('width only – DPR strategy', () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + width: 400, + }); + // When width is provided without sizes attribute, the DPR strategy should be used. + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 1x, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 2x", + sizes: undefined, + width: 400 + }); + }); + + it('custom breakpoints', () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + deviceSizes: [200, 400, 800], + imageSizes: [100], + }); + // For custom breakpoints, the breakpoints will be derived from the provided arrays. + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-800,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-200,c-at_max 200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-400,c-at_max 400w, https://ik.imagekit.io/demo/sample.jpg?tr=w-800,c-at_max 800w", + sizes: "100vw", + width: undefined + }); + }); + + it('preserves caller transformations', () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + width: 500, + transformation: [{ height: 300 }], + }); + // The provided transformation should be preserved in the output. + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/sample.jpg?tr=height-300,w-1200,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=height-300,w-640,c-at_max 1x, https://ik.imagekit.io/demo/sample.jpg?tr=height-300,w-1200,c-at_max 2x", + sizes: undefined, + width: 500 + }); + }); + + it('both sizes and width passed', () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + sizes: '50vw', + width: 600, + }); + // Both sizes and width are provided, so the function should apply the sizes attribute while using width for DPR strategy. + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", + sizes: "50vw", + width: 600 + }); + }); + + it('multiple transformations', () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + width: 450, + transformation: [ + { height: 300 }, + { aiRemoveBackground: true } + ] + }); + // Multiple caller transformations should be combined appropriately. + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/sample.jpg?tr=height-300,aiRemoveBackground-true,w-828,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=height-300,aiRemoveBackground-true,w-640,c-at_max 1x, https://ik.imagekit.io/demo/sample.jpg?tr=height-300,aiRemoveBackground-true,w-828,c-at_max 2x", + sizes: undefined, + width: 450 + }); + }); + + it('sizes causes breakpoint pruning (33vw path)', () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + sizes: '(min-width: 800px) 33vw, 100vw', + }); + // When specified with a sizes attribute that prunes breakpoints, the output should reflect the pruned values. + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", + sizes: "(min-width: 800px) 33vw, 100vw", + width: undefined + }); + }); +}); From b7a5758843433427e445b679cd26a341a9e1f441 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 5 May 2025 16:16:58 +0530 Subject: [PATCH 03/10] fix test cases --- src/getResponsiveImageAttributes.ts | 8 +++-- test/getResponsiveImageAttributes.test.js | 41 ++++++++++------------- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/src/getResponsiveImageAttributes.ts b/src/getResponsiveImageAttributes.ts index 3752121..5abf1e3 100644 --- a/src/getResponsiveImageAttributes.ts +++ b/src/getResponsiveImageAttributes.ts @@ -3,7 +3,7 @@ import { buildSrc } from './url' /* Default break‑point pools */ const DEFAULT_DEVICE_BREAKPOINTS = [640, 750, 828, 1080, 1200, 1920, 2048, 3840] as const -const DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 320, 420] as const +const DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 384] as const export interface GetImageAttributesOptions extends SrcOptions { width?: number // explicit rendered width @@ -62,11 +62,13 @@ export function getResponsiveImageAttributes( .map((w, i) => `${buildURL(w)} ${descriptorKind === 'w' ? w : i + 1}${descriptorKind}`) .join(', ') || undefined + const finalSizes = sizes ?? (descriptorKind === 'w' ? '100vw' : undefined) + return { src: buildURL(candidates[candidates.length - 1]), // largest candidate srcSet, - sizes: sizes ?? (descriptorKind === 'w' ? '100vw' : undefined), - width, + ...(finalSizes ? { sizes: finalSizes } : {}), // include only when defined + ...(width !== undefined ? { width } : {}), // include only when defined } } diff --git a/test/getResponsiveImageAttributes.test.js b/test/getResponsiveImageAttributes.test.js index b6a4db8..9f6aa67 100644 --- a/test/getResponsiveImageAttributes.test.js +++ b/test/getResponsiveImageAttributes.test.js @@ -1,18 +1,17 @@ const { expect } = require('chai'); const { getResponsiveImageAttributes } = require('../src/getResponsiveImageAttributes'); -describe.only('getResponsiveImageAttributes – smoke run‑through', () => { +describe.only('getResponsiveImageAttributes', () => { it('bare minimum input', () => { const out = getResponsiveImageAttributes({ src: 'sample.jpg', urlEndpoint: 'https://ik.imagekit.io/demo', }); - // Expected object based on default deviceSizes and imageSizes: + // Expected object based on default deviceBreakpoints and imageBreakpoints: expect(out).to.deep.equal({ src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max", srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", - sizes: "100vw", - width: undefined + sizes: "100vw" }); }); @@ -26,8 +25,7 @@ describe.only('getResponsiveImageAttributes – smoke run‑through', () => { expect(out).to.deep.equal({ src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max", srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", - sizes: "100vw", - width: undefined + sizes: "100vw" }); }); @@ -39,9 +37,8 @@ describe.only('getResponsiveImageAttributes – smoke run‑through', () => { }); // When width is provided without sizes attribute, the DPR strategy should be used. expect(out).to.deep.equal({ - src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max", - srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 1x, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 2x", - sizes: undefined, + src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 1x, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 2x", width: 400 }); }); @@ -50,15 +47,14 @@ describe.only('getResponsiveImageAttributes – smoke run‑through', () => { const out = getResponsiveImageAttributes({ src: 'sample.jpg', urlEndpoint: 'https://ik.imagekit.io/demo', - deviceSizes: [200, 400, 800], - imageSizes: [100], + deviceBreakpoints: [200, 400, 800], + imageBreakpoints: [100], }); // For custom breakpoints, the breakpoints will be derived from the provided arrays. expect(out).to.deep.equal({ src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-800,c-at_max", srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-200,c-at_max 200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-400,c-at_max 400w, https://ik.imagekit.io/demo/sample.jpg?tr=w-800,c-at_max 800w", - sizes: "100vw", - width: undefined + sizes: "100vw" }); }); @@ -71,9 +67,8 @@ describe.only('getResponsiveImageAttributes – smoke run‑through', () => { }); // The provided transformation should be preserved in the output. expect(out).to.deep.equal({ - src: "https://ik.imagekit.io/demo/sample.jpg?tr=height-300,w-1200,c-at_max", - srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=height-300,w-640,c-at_max 1x, https://ik.imagekit.io/demo/sample.jpg?tr=height-300,w-1200,c-at_max 2x", - sizes: undefined, + src: "https://ik.imagekit.io/demo/sample.jpg?tr=h-300:w-1080,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=h-300:w-640,c-at_max 1x, https://ik.imagekit.io/demo/sample.jpg?tr=h-300:w-1080,c-at_max 2x", width: 500 }); }); @@ -87,8 +82,8 @@ describe.only('getResponsiveImageAttributes – smoke run‑through', () => { }); // Both sizes and width are provided, so the function should apply the sizes attribute while using width for DPR strategy. expect(out).to.deep.equal({ - src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max", - srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", + src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-384,c-at_max 384w, https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", sizes: "50vw", width: 600 }); @@ -106,9 +101,8 @@ describe.only('getResponsiveImageAttributes – smoke run‑through', () => { }); // Multiple caller transformations should be combined appropriately. expect(out).to.deep.equal({ - src: "https://ik.imagekit.io/demo/sample.jpg?tr=height-300,aiRemoveBackground-true,w-828,c-at_max", - srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=height-300,aiRemoveBackground-true,w-640,c-at_max 1x, https://ik.imagekit.io/demo/sample.jpg?tr=height-300,aiRemoveBackground-true,w-828,c-at_max 2x", - sizes: undefined, + src: "https://ik.imagekit.io/demo/sample.jpg?tr=h-300:e-bgremove:w-1080,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=h-300:e-bgremove:w-640,c-at_max 1x, https://ik.imagekit.io/demo/sample.jpg?tr=h-300:e-bgremove:w-1080,c-at_max 2x", width: 450 }); }); @@ -122,9 +116,8 @@ describe.only('getResponsiveImageAttributes – smoke run‑through', () => { // When specified with a sizes attribute that prunes breakpoints, the output should reflect the pruned values. expect(out).to.deep.equal({ src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max", - srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", - sizes: "(min-width: 800px) 33vw, 100vw", - width: undefined + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-256,c-at_max 256w, https://ik.imagekit.io/demo/sample.jpg?tr=w-384,c-at_max 384w, https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", + sizes: "(min-width: 800px) 33vw, 100vw" }); }); }); From 2ad35ebb39a43170218f771019cc2296de5a035f Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 5 May 2025 16:31:27 +0530 Subject: [PATCH 04/10] add jsdocs and add test for query parameters --- src/getResponsiveImageAttributes.ts | 48 +++++++++++++++++++---- test/getResponsiveImageAttributes.test.js | 22 +++++++++++ 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/src/getResponsiveImageAttributes.ts b/src/getResponsiveImageAttributes.ts index 5abf1e3..731794d 100644 --- a/src/getResponsiveImageAttributes.ts +++ b/src/getResponsiveImageAttributes.ts @@ -6,12 +6,39 @@ const DEFAULT_DEVICE_BREAKPOINTS = [640, 750, 828, 1080, 1200, 1920, 2048, 3840] const DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 384] as const export interface GetImageAttributesOptions extends SrcOptions { - width?: number // explicit rendered width - sizes?: string // the HTML sizes value - deviceBreakpoints?: number[] // override default device break‑points - imageBreakpoints?: number[] // override tiny image break‑points + /** + * The intended display width (in pixels) of the image on screen. + * Used for calculating `srcSet` with a pixel-density (DPR) strategy. + * If omitted, a width-based strategy using breakpoints will be applied. + */ + width?: number + + /** + * The `sizes` attribute for the image element. + * Typically used to indicate how the image will scale across different viewport sizes (e.g., "100vw"). + * Presence of `sizes` triggers a width-based `srcSet` strategy. + */ + sizes?: string + + /** + * An optional custom list of device width breakpoints (in pixels). + * If not specified, defaults to `[640, 750, 828, 1080, 1200, 1920, 2048, 3840]`. + * Recommended to align with your target audience's common screen widths. + */ + deviceBreakpoints?: number[] + + /** + * An optional list of custom image breakpoints (in pixels). + * These are merged with the device breakpoints to compute the final list of candidate widths. + * Defaults to `[16, 32, 48, 64, 96, 128, 256, 384]`. + */ + imageBreakpoints?: number[] } +/** + * Resulting set of attributes suitable for an HTML `` element. + * Useful for enabling responsive image loading. + */ export interface ResponsiveImageAttributes { src: string srcSet?: string @@ -19,6 +46,10 @@ export interface ResponsiveImageAttributes { width?: number } +/** + * Generates a responsive image URL, `srcSet`, and `sizes` attributes + * based on the input options such as `width`, `sizes`, and breakpoints. + */ export function getResponsiveImageAttributes( opts: GetImageAttributesOptions ): ResponsiveImageAttributes { @@ -80,7 +111,7 @@ function computeCandidateWidths(params: { }): { candidates: number[]; descriptorKind: 'w' | 'x' } { const { allBreakpoints, deviceBreakpoints, explicitWidth, sizesAttr } = params - /* --- sizes attribute present ----------------------------------- */ + // Strategy 1: Width-based srcSet (`w`) using viewport `vw` hints if (sizesAttr) { const vwTokens = sizesAttr.match(/(^|\s)(1?\d{1,2})vw/g) || [] const vwPercents = vwTokens.map((t) => parseInt(t, 10)) @@ -93,16 +124,17 @@ function computeCandidateWidths(params: { descriptorKind: 'w', } } - /* no vw → give the full break‑point list */ + + // No usable `vw` found: fallback to all breakpoints return { candidates: allBreakpoints, descriptorKind: 'w' } } - /* --- no sizes attr ------------------------------------------------ */ + // Strategy 2: Fallback using explicit image width using device breakpoints if (typeof explicitWidth !== 'number') { return { candidates: deviceBreakpoints, descriptorKind: 'w' } } - /* DPR strategy: 1× & 2× nearest break‑points */ + // Strategy 3: Use 1x and 2x nearest breakpoints for `x` descriptor const nearest = (t: number) => allBreakpoints.find((n) => n >= t) || allBreakpoints[allBreakpoints.length - 1] diff --git a/test/getResponsiveImageAttributes.test.js b/test/getResponsiveImageAttributes.test.js index 9f6aa67..75c4435 100644 --- a/test/getResponsiveImageAttributes.test.js +++ b/test/getResponsiveImageAttributes.test.js @@ -120,4 +120,26 @@ describe.only('getResponsiveImageAttributes', () => { sizes: "(min-width: 800px) 33vw, 100vw" }); }); + + it("Using queryParameters and transformationPosition", () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + width: 450, + transformation: [ + { height: 300 }, + { aiRemoveBackground: true } + ], + queryParameters: { + key: "value" + }, + transformationPosition: "path" + }); + // The function should respect the transformation position and query parameters. + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/tr:h-300:e-bgremove:w-1080,c-at_max/sample.jpg?key=value", + srcSet: "https://ik.imagekit.io/demo/tr:h-300:e-bgremove:w-640,c-at_max/sample.jpg?key=value 1x, https://ik.imagekit.io/demo/tr:h-300:e-bgremove:w-1080,c-at_max/sample.jpg?key=value 2x", + width: 450 + }); + }) }); From e543d8ef3776e83268fcb699aed64a4f34a92726 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 5 May 2025 16:31:43 +0530 Subject: [PATCH 05/10] fix: remove .only from describe block in getResponsiveImageAttributes tests --- test/getResponsiveImageAttributes.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/getResponsiveImageAttributes.test.js b/test/getResponsiveImageAttributes.test.js index 75c4435..72da008 100644 --- a/test/getResponsiveImageAttributes.test.js +++ b/test/getResponsiveImageAttributes.test.js @@ -1,7 +1,7 @@ const { expect } = require('chai'); const { getResponsiveImageAttributes } = require('../src/getResponsiveImageAttributes'); -describe.only('getResponsiveImageAttributes', () => { +describe('getResponsiveImageAttributes', () => { it('bare minimum input', () => { const out = getResponsiveImageAttributes({ src: 'sample.jpg', From a96d92526c3b9884613419f4a87757feffbd387d Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 5 May 2025 16:46:00 +0530 Subject: [PATCH 06/10] test: add fallback case for no usable vw tokens in getResponsiveImageAttributes --- test/getResponsiveImageAttributes.test.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/getResponsiveImageAttributes.test.js b/test/getResponsiveImageAttributes.test.js index 72da008..28ad5da 100644 --- a/test/getResponsiveImageAttributes.test.js +++ b/test/getResponsiveImageAttributes.test.js @@ -142,4 +142,18 @@ describe('getResponsiveImageAttributes', () => { width: 450 }); }) + + it("fallback when no usable vw tokens", () => { + const out = getResponsiveImageAttributes({ + src: 'sample.jpg', + urlEndpoint: 'https://ik.imagekit.io/demo', + sizes: "100%" + }); + expect(out).to.deep.equal({ + src: "https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max", + srcSet: "https://ik.imagekit.io/demo/sample.jpg?tr=w-16,c-at_max 16w, https://ik.imagekit.io/demo/sample.jpg?tr=w-32,c-at_max 32w, https://ik.imagekit.io/demo/sample.jpg?tr=w-48,c-at_max 48w, https://ik.imagekit.io/demo/sample.jpg?tr=w-64,c-at_max 64w, https://ik.imagekit.io/demo/sample.jpg?tr=w-96,c-at_max 96w, https://ik.imagekit.io/demo/sample.jpg?tr=w-128,c-at_max 128w, https://ik.imagekit.io/demo/sample.jpg?tr=w-256,c-at_max 256w, https://ik.imagekit.io/demo/sample.jpg?tr=w-384,c-at_max 384w, https://ik.imagekit.io/demo/sample.jpg?tr=w-640,c-at_max 640w, https://ik.imagekit.io/demo/sample.jpg?tr=w-750,c-at_max 750w, https://ik.imagekit.io/demo/sample.jpg?tr=w-828,c-at_max 828w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1080,c-at_max 1080w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1200,c-at_max 1200w, https://ik.imagekit.io/demo/sample.jpg?tr=w-1920,c-at_max 1920w, https://ik.imagekit.io/demo/sample.jpg?tr=w-2048,c-at_max 2048w, https://ik.imagekit.io/demo/sample.jpg?tr=w-3840,c-at_max 3840w", + sizes: "100%" + }); + }) + }); From 2575dc46c5f58451fd32fde6b5e110c08fca7e22 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 5 May 2025 16:51:19 +0530 Subject: [PATCH 07/10] fix: sort image breakpoints before merging with device breakpoints in getResponsiveImageAttributes --- src/getResponsiveImageAttributes.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/getResponsiveImageAttributes.ts b/src/getResponsiveImageAttributes.ts index 731794d..3240cdf 100644 --- a/src/getResponsiveImageAttributes.ts +++ b/src/getResponsiveImageAttributes.ts @@ -65,11 +65,13 @@ export function getResponsiveImageAttributes( imageBreakpoints = DEFAULT_IMAGE_BREAKPOINTS as unknown as number[], } = opts - const allBreakpoints = [...imageBreakpoints, ...deviceBreakpoints].sort((a, b) => a - b) + const sortedDeviceBreakpoints = [...deviceBreakpoints].sort((a, b) => a - b); + const sortedImageBreakpoints = [...imageBreakpoints].sort((a, b) => a - b); + const allBreakpoints = [...sortedImageBreakpoints, ...sortedDeviceBreakpoints].sort((a, b) => a - b); const { candidates, descriptorKind } = computeCandidateWidths({ allBreakpoints, - deviceBreakpoints, + deviceBreakpoints: sortedDeviceBreakpoints, explicitWidth: width, sizesAttr: sizes, }) From a86f5159cb00ab5104340991f2e8d014b69d993c Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 5 May 2025 17:17:12 +0530 Subject: [PATCH 08/10] feat: add background and gradient properties to SolidColorOverlayTransformation type --- src/interfaces/Transformation.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/interfaces/Transformation.ts b/src/interfaces/Transformation.ts index b45be9f..6396b37 100644 --- a/src/interfaces/Transformation.ts +++ b/src/interfaces/Transformation.ts @@ -720,4 +720,19 @@ export type SolidColorOverlayTransformation = Pick Date: Mon, 5 May 2025 17:17:23 +0530 Subject: [PATCH 09/10] refactor: enhance documentation for GetImageAttributesOptions and ResponsiveImageAttributes interfaces --- src/getResponsiveImageAttributes.ts | 40 ++++++++++++++++++++--------- src/index.ts | 4 ++- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/getResponsiveImageAttributes.ts b/src/getResponsiveImageAttributes.ts index 3240cdf..3b3c120 100644 --- a/src/getResponsiveImageAttributes.ts +++ b/src/getResponsiveImageAttributes.ts @@ -7,30 +7,42 @@ const DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 384] as const export interface GetImageAttributesOptions extends SrcOptions { /** - * The intended display width (in pixels) of the image on screen. - * Used for calculating `srcSet` with a pixel-density (DPR) strategy. - * If omitted, a width-based strategy using breakpoints will be applied. - */ + * The intended display width of the image in pixels, + * used **only when the `sizes` attribute is not provided**. + * + * Triggers a DPR-based strategy (1x and 2x variants) and generates `x` descriptors in `srcSet`. + * + * Ignored if `sizes` is present. + */ width?: number /** - * The `sizes` attribute for the image element. - * Typically used to indicate how the image will scale across different viewport sizes (e.g., "100vw"). - * Presence of `sizes` triggers a width-based `srcSet` strategy. + * The value for the HTML `sizes` attribute + * (e.g., `"100vw"` or `"(min-width:768px) 50vw, 100vw"`). + * + * - If it includes one or more `vw` units, breakpoints smaller than the corresponding percentage of the smallest device width are excluded. + * - If it contains no `vw` units, the full breakpoint list is used. + * + * Enables a width-based strategy and generates `w` descriptors in `srcSet`. */ sizes?: string /** - * An optional custom list of device width breakpoints (in pixels). - * If not specified, defaults to `[640, 750, 828, 1080, 1200, 1920, 2048, 3840]`. - * Recommended to align with your target audience's common screen widths. + * Custom list of **device-width breakpoints** in pixels. + * These define common screen widths for responsive image generation. + * + * Defaults to `[640, 750, 828, 1080, 1200, 1920, 2048, 3840]`. + * Sorted automatically. */ deviceBreakpoints?: number[] /** - * An optional list of custom image breakpoints (in pixels). - * These are merged with the device breakpoints to compute the final list of candidate widths. + * Custom list of **image-specific breakpoints** in pixels. + * Useful for generating small variants (e.g., placeholders or thumbnails). + * + * Merged with `deviceBreakpoints` before calculating `srcSet`. * Defaults to `[16, 32, 48, 64, 96, 128, 256, 384]`. + * Sorted automatically. */ imageBreakpoints?: number[] } @@ -40,9 +52,13 @@ export interface GetImageAttributesOptions extends SrcOptions { * Useful for enabling responsive image loading. */ export interface ResponsiveImageAttributes { + /** URL for the *largest* candidate (assigned to plain `src`). */ src: string + /** Candidate set with `w` or `x` descriptors. */ srcSet?: string + /** `sizes` returned (or synthesised as `100vw`). */ sizes?: string + /** Width as a number (if `width` was provided). */ width?: number } diff --git a/src/index.ts b/src/index.ts index 321c824..51ec6b0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,11 +2,13 @@ import type { SrcOptions, Transformation, UploadOptions, UploadResponse } from " import { ImageKitAbortError, ImageKitInvalidRequestError, ImageKitServerError, ImageKitUploadNetworkError, upload } from "./upload"; import { buildSrc, buildTransformationString } from "./url"; import { getResponsiveImageAttributes } from "./getResponsiveImageAttributes"; +import type { GetImageAttributesOptions, ResponsiveImageAttributes } from "./getResponsiveImageAttributes"; export { buildSrc, buildTransformationString, upload, getResponsiveImageAttributes, ImageKitInvalidRequestError, ImageKitAbortError, ImageKitServerError, ImageKitUploadNetworkError }; export type { Transformation, SrcOptions, UploadOptions, - UploadResponse + UploadResponse, + GetImageAttributesOptions, ResponsiveImageAttributes }; From ec15969ad8233dea73407c32ddc404fc12569af0 Mon Sep 17 00:00:00 2001 From: Manu Chaudhary Date: Mon, 5 May 2025 17:25:34 +0530 Subject: [PATCH 10/10] chore: update version to 5.1.0-beta.1 and document new helper getResponsiveImageAttributes --- CHANGELOG.md | 9 +++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 220aee1..b184c0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## Version 5.1.0 + +1. **New helper** `getResponsiveImageAttributes()` + Generates ready‑to‑use `src`, `srcSet`, and `sizes` for responsive `` tags (breakpoint pruning, DPR 1×/2×, custom breakpoints, no up‑scaling). +2. Added exports: + `getResponsiveImageAttributes`, `GetImageAttributesOptions`, `ResponsiveImageAttributes`. + +_No breaking changes from 5.0.x._ + ## Version 5.0.0 This version introduces major breaking changes, for usage examples, refer to the [official documentation](https://imagekit.io/docs/integration/javascript). diff --git a/package-lock.json b/package-lock.json index aa14b58..cc7eafd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@imagekit/javascript", - "version": "5.0.0", + "version": "5.1.0-beta.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@imagekit/javascript", - "version": "5.0.0", + "version": "5.1.0-beta.1", "license": "MIT", "devDependencies": { "@babel/cli": "^7.10.5", diff --git a/package.json b/package.json index 8d007cb..f1d3e66 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@imagekit/javascript", - "version": "5.0.0", + "version": "5.1.0-beta.1", "description": "ImageKit Javascript SDK", "main": "dist/imagekit.cjs.js", "module": "dist/imagekit.esm.js",