@shopware/helpers 1.6.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,31 +30,108 @@ export default defineConfig({
30
30
  });
31
31
  ```
32
32
 
33
- <!-- AUTO GENERATED CHANGELOG -->
33
+ ## `getBackgroundImageUrl` helper
34
34
 
35
- ## Changelog
35
+ The `getBackgroundImageUrl` function generates optimized CSS `url()` values for CMS background images. It extracts the raw URL, calculates the appropriate dimensions based on the media metadata, and applies image transformation parameters.
36
36
 
37
- Full changelog for stable version is available [here](https://github.com/shopware/frontends/blob/main/packages/helpers/CHANGELOG.md)
37
+ ### Usage
38
+
39
+ ```ts
40
+ import { getBackgroundImageUrl } from "@shopware/helpers";
41
+
42
+ const optimizedUrl = getBackgroundImageUrl(
43
+ "url(https://cdn.shopware.store/.../image.jpg)",
44
+ cmsBlockOrSection, // object with backgroundMedia.metaData.width/height
45
+ { format: "webp", quality: 85 }, // optional
46
+ );
47
+ // => 'url("https://cdn.shopware.store/.../image.jpg?width=1000&fit=crop,smart&format=webp&quality=85")'
48
+ ```
38
49
 
39
- ### Latest changes: 1.6.0
50
+ ### Parameters
40
51
 
41
- ### Minor Changes
52
+ | Parameter | Type | Description |
53
+ |-----------|------|-------------|
54
+ | `url` | `string` | CSS `url()` string containing the background image URL |
55
+ | `element` | `{ backgroundMedia?: { metaData?: { width?: number; height?: number } } }` | CMS section or block object with media metadata |
56
+ | `options` | `BackgroundImageOptions` (optional) | Format and quality settings |
42
57
 
43
- - [#1985](https://github.com/shopware/frontends/pull/1985) [`2cbda25`](https://github.com/shopware/frontends/commit/2cbda257a1056454e12f2fba9052f83eecb6d986) Thanks [@mkucmus](https://github.com/mkucmus)! - Added methods to extract product info:
44
- - `isProductOnSale`
45
- - `isProductTopSeller`
46
- - `getProductManufacturerName`
58
+ ### `BackgroundImageOptions`
47
59
 
48
- - [#2176](https://github.com/shopware/frontends/pull/2176) [`c647baf`](https://github.com/shopware/frontends/commit/c647baf93e7174b849f5961ee5803add99d78602) Thanks [@mkucmus](https://github.com/mkucmus)! - - Add `getProductListingFromCmsPage` helper to extract product listing from CMS page structure
49
- - Enable early access to listing data during SSR before component tree renders
60
+ ```ts
61
+ type BackgroundImageOptions = {
62
+ format?: string; // "webp" | "avif" | "jpg" | "png"
63
+ quality?: number; // 0-100
64
+ };
65
+ ```
50
66
 
51
- ### Patch Changes
67
+ When `format` or `quality` are provided, they are appended as query parameters to the image URL. If omitted, only the dimension and fit parameters are applied.
68
+
69
+ ## `generateCdnSrcSet` helper
70
+
71
+ Generates an HTML `srcset` string using CDN width-based resizing. Useful as a fallback when media has no pre-generated thumbnails — the CDN handles on-the-fly resizing via query parameters.
72
+
73
+ ### Usage
74
+
75
+ ```ts
76
+ import { generateCdnSrcSet } from "@shopware/helpers";
77
+
78
+ const srcset = generateCdnSrcSet(
79
+ "https://cdn.shopware.store/.../image.jpg",
80
+ [400, 800, 1200, 1600], // optional, these are the defaults
81
+ { format: "webp", quality: 85 }, // optional
82
+ );
83
+ // => "https://cdn.shopware.store/.../image.jpg?width=400&fit=crop,smart&format=webp&quality=85 400w, ...800w, ...1200w, ...1600w"
84
+ ```
85
+
86
+ ### Parameters
87
+
88
+ | Parameter | Type | Description |
89
+ |-----------|------|-------------|
90
+ | `src` | `string \| undefined` | Base image URL |
91
+ | `widths` | `number[]` (optional) | Array of widths to generate (default: `[400, 800, 1200, 1600]`) |
92
+ | `options` | `{ format?: string; quality?: number }` (optional) | Format and quality settings |
93
+
94
+ Returns `undefined` if `src` is falsy or URL parsing fails.
52
95
 
53
- - [#2030](https://github.com/shopware/frontends/pull/2030) [`22ff62e`](https://github.com/shopware/frontends/commit/22ff62e354f024599d64ea8096af57695248851c) Thanks [@mkucmus](https://github.com/mkucmus)! - Extended `ListingFilter` type to support property options and manufacturer entities:
54
- - Added optional `options` property for property group options
55
- - Added optional `entities` property for manufacturer entities
56
- - Improved type safety by explicitly typing empty array return in `getListingFilters()`
96
+ ## `buildCdnImageUrl` helper
97
+
98
+ Builds an optimized CDN image URL with size parameters based on rendered element dimensions. Adds `width` or `height` (whichever is larger) rounded up to the nearest 100px, plus `fit=crop,smart`.
99
+
100
+ ### Usage
101
+
102
+ ```ts
103
+ import { buildCdnImageUrl } from "@shopware/helpers";
104
+
105
+ const url = buildCdnImageUrl(
106
+ "https://cdn.shopware.store/.../image.jpg",
107
+ { width: 724, height: 760 },
108
+ );
109
+ // => "https://cdn.shopware.store/.../image.jpg?height=800&fit=crop,smart"
110
+ ```
111
+
112
+ ### Parameters
113
+
114
+ | Parameter | Type | Description |
115
+ |-----------|------|-------------|
116
+ | `src` | `string \| undefined` | Base image URL |
117
+ | `dimensions` | `{ width: number; height: number }` | Rendered element dimensions in pixels |
118
+ | `options` | `{ format?: string; quality?: number }` (optional) | Format and quality settings |
119
+
120
+ Returns an empty string if `src` is falsy. Returns the original `src` if URL parsing fails.
121
+
122
+ <!-- AUTO GENERATED CHANGELOG -->
123
+
124
+ ## Changelog
125
+
126
+ Full changelog for stable version is available [here](https://github.com/shopware/frontends/blob/main/packages/helpers/CHANGELOG.md)
127
+
128
+ ### Latest changes: 1.7.1
129
+
130
+ ### Patch Changes
57
131
 
58
- - [#2153](https://github.com/shopware/frontends/pull/2153) [`56cd178`](https://github.com/shopware/frontends/commit/56cd178e25fe2399b7170ccac3044e980621f041) Thanks [@mkucmus](https://github.com/mkucmus)! - Fix `getSrcSetForMedia` to properly encode special characters (spaces, commas, parentheses) in media URLs
132
+ - [#2372](https://github.com/shopware/frontends/pull/2372) [`22fc8a7`](https://github.com/shopware/frontends/commit/22fc8a7301f6a7d2612d907ab73555978b651c00) Thanks [@patzick](https://github.com/patzick)! - Improve technical URL resolution for SSR and CSR page rendering.
59
133
 
60
- - [#2162](https://github.com/shopware/frontends/pull/2162) [`e1fae3e`](https://github.com/shopware/frontends/commit/e1fae3eb6430e5c8e133456fbaf7f215f80c36f6) Thanks [@mkucmus](https://github.com/mkucmus)! - Export encodeUrlPath helper function to properly handle special characters (spaces, commas) in image URLs
134
+ This adds helpers to detect and normalize technical Shopware paths and updates
135
+ `useNavigationSearch` to resolve `/navigation/*`, `/detail/*`, and
136
+ `/landingPage/*` routes more reliably, including fallback behavior when no SEO
137
+ mapping row is returned.
package/dist/index.d.mts CHANGED
@@ -397,6 +397,10 @@ type LayoutConfiguration = {
397
397
  */
398
398
  declare function getCmsLayoutConfiguration<T extends CmsBlock | CmsSection | CmsSlot>(content: T): LayoutConfiguration;
399
399
 
400
+ type BackgroundImageOptions = {
401
+ format?: string;
402
+ quality?: number;
403
+ };
400
404
  declare function getBackgroundImageUrl<T extends {
401
405
  backgroundMedia?: {
402
406
  metaData?: {
@@ -404,7 +408,7 @@ declare function getBackgroundImageUrl<T extends {
404
408
  height?: number;
405
409
  };
406
410
  };
407
- }>(url: string, element: T): string;
411
+ }>(url: string, element: T, options?: BackgroundImageOptions): string;
408
412
 
409
413
  type UrlRoute = {
410
414
  path?: string;
@@ -522,6 +526,20 @@ declare function isLandingPage<T extends {
522
526
  apiAlias: string;
523
527
  }>(entity: T | LandingPage): entity is LandingPage;
524
528
 
529
+ declare const TECHNICAL_ROUTE_MAP: {
530
+ readonly "/navigation/": "frontend.navigation.page";
531
+ readonly "/detail/": "frontend.detail.page";
532
+ readonly "/landingPage/": "frontend.landing.page";
533
+ };
534
+ type RouteNameFromPathInfo = (typeof TECHNICAL_ROUTE_MAP)[keyof typeof TECHNICAL_ROUTE_MAP];
535
+ type RouteInfoFromPathInfo = {
536
+ routeName: RouteNameFromPathInfo;
537
+ foreignKey: string;
538
+ };
539
+ declare function normalizePath(path: string): string;
540
+ declare function isTechnicalPath(path: string): boolean;
541
+ declare function getRouteFromPathInfo(path: string): RouteInfoFromPathInfo | null;
542
+
525
543
  /**
526
544
  * Get translated property from the given object.
527
545
  *
@@ -616,7 +634,7 @@ declare function getMedia<T extends {
616
634
  }>(lineItem: T): ProductMedia[];
617
635
 
618
636
  /**
619
- * Returns the smallest thumbnail url from the media object
637
+ * Returns the smallest thumbnail url from the media object or the media.url if no thumbnails are available
620
638
  *
621
639
  * @param media image object
622
640
  *
@@ -628,6 +646,7 @@ declare function getSmallestThumbnailUrl<T extends {
628
646
  width: number;
629
647
  url: string;
630
648
  }>;
649
+ url?: string;
631
650
  }>(media?: T): string | undefined;
632
651
 
633
652
  /**
@@ -640,6 +659,41 @@ declare function getSmallestThumbnailUrl<T extends {
640
659
  * @category Media
641
660
  */
642
661
  declare function encodeUrlPath(urlString: string): string;
662
+ /**
663
+ * Generates a srcset string using CDN width-based resizing.
664
+ * Useful as a fallback when media has no pre-generated thumbnails.
665
+ *
666
+ * @param src - Base image URL
667
+ * @param widths - Array of widths to generate (default: [400, 800, 1200, 1600])
668
+ * @param options - Optional format and quality params
669
+ * @returns srcset string or undefined if src is invalid
670
+ *
671
+ * @public
672
+ * @category Media
673
+ */
674
+ declare function generateCdnSrcSet(src: string | undefined, widths?: number[], options?: {
675
+ format?: string;
676
+ quality?: number;
677
+ }): string | undefined;
678
+ /**
679
+ * Builds an optimized CDN image URL with size parameters.
680
+ * Adds width or height (whichever is larger, width if equal)
681
+ * rounded up to the nearest 100px.
682
+ *
683
+ * @param src - Base image URL
684
+ * @param dimensions - Rendered element dimensions (width and height)
685
+ * @returns Optimized image URL string
686
+ *
687
+ * @public
688
+ * @category Media
689
+ */
690
+ declare function buildCdnImageUrl(src: string | undefined, dimensions: {
691
+ width: number;
692
+ height: number;
693
+ }, options?: {
694
+ format?: string;
695
+ quality?: number;
696
+ }): string;
643
697
  /**
644
698
  * Returns the srcset attribute for the image, for available breakpoints
645
699
  *
@@ -715,4 +769,5 @@ declare function canUseQuoteActions<T extends {
715
769
  };
716
770
  }>(quote: T): boolean;
717
771
 
718
- export { type HelpersCssClasses, type LayoutConfiguration, type TierPrice, type UiMediaGalleryItem, type UiMediaGalleryItemUrl, type UiProductOption, type UiProductProperty, type UiProductReview, type UrlRouteOutput, buildUrlPrefix, canUseQuoteActions, downloadFile, encodeUrlPath, getBackgroundImageUrl, getBiggestThumbnailUrl, getCategoryBreadcrumbs, getCategoryImageUrl, getCategoryRoute, getCategoryUrl, getCmsBreadcrumbs, getCmsEntityObject, getCmsLayoutConfiguration, getCmsTranslate, getFormattedPrice, getLanguageName, getListingFilters, getMainImageUrl, getMedia, getPaymentMethodIcon, getProductCalculatedListingPrice, getProductFreeShipping, getProductFromPrice, getProductListingFromCmsPage, getProductManufacturerName, getProductName, getProductRatingAverage, getProductReviews, getProductRoute, getProductTierPrices, getProductUrl, getShippingMethodDeliveryTime, getShippingMethodIcon, getSmallestThumbnailUrl, getSrcSetForMedia, getTranslatedProperty, helpersCssClasses, isCategory, isLandingPage, isMaintenanceMode, isProduct, isProductOnSale, isProductTopSeller, relativeUrlSlash, urlIsAbsolute };
772
+ export { buildCdnImageUrl, buildUrlPrefix, canUseQuoteActions, downloadFile, encodeUrlPath, generateCdnSrcSet, getBackgroundImageUrl, getBiggestThumbnailUrl, getCategoryBreadcrumbs, getCategoryImageUrl, getCategoryRoute, getCategoryUrl, getCmsBreadcrumbs, getCmsEntityObject, getCmsLayoutConfiguration, getCmsTranslate, getFormattedPrice, getLanguageName, getListingFilters, getMainImageUrl, getMedia, getPaymentMethodIcon, getProductCalculatedListingPrice, getProductFreeShipping, getProductFromPrice, getProductListingFromCmsPage, getProductManufacturerName, getProductName, getProductRatingAverage, getProductReviews, getProductRoute, getProductTierPrices, getProductUrl, getRouteFromPathInfo, getShippingMethodDeliveryTime, getShippingMethodIcon, getSmallestThumbnailUrl, getSrcSetForMedia, getTranslatedProperty, helpersCssClasses, isCategory, isLandingPage, isMaintenanceMode, isProduct, isProductOnSale, isProductTopSeller, isTechnicalPath, normalizePath, relativeUrlSlash, urlIsAbsolute };
773
+ export type { BackgroundImageOptions, HelpersCssClasses, LayoutConfiguration, RouteInfoFromPathInfo, RouteNameFromPathInfo, TierPrice, UiMediaGalleryItem, UiMediaGalleryItemUrl, UiProductOption, UiProductProperty, UiProductReview, UrlRouteOutput };
package/dist/index.d.ts CHANGED
@@ -397,6 +397,10 @@ type LayoutConfiguration = {
397
397
  */
398
398
  declare function getCmsLayoutConfiguration<T extends CmsBlock | CmsSection | CmsSlot>(content: T): LayoutConfiguration;
399
399
 
400
+ type BackgroundImageOptions = {
401
+ format?: string;
402
+ quality?: number;
403
+ };
400
404
  declare function getBackgroundImageUrl<T extends {
401
405
  backgroundMedia?: {
402
406
  metaData?: {
@@ -404,7 +408,7 @@ declare function getBackgroundImageUrl<T extends {
404
408
  height?: number;
405
409
  };
406
410
  };
407
- }>(url: string, element: T): string;
411
+ }>(url: string, element: T, options?: BackgroundImageOptions): string;
408
412
 
409
413
  type UrlRoute = {
410
414
  path?: string;
@@ -522,6 +526,20 @@ declare function isLandingPage<T extends {
522
526
  apiAlias: string;
523
527
  }>(entity: T | LandingPage): entity is LandingPage;
524
528
 
529
+ declare const TECHNICAL_ROUTE_MAP: {
530
+ readonly "/navigation/": "frontend.navigation.page";
531
+ readonly "/detail/": "frontend.detail.page";
532
+ readonly "/landingPage/": "frontend.landing.page";
533
+ };
534
+ type RouteNameFromPathInfo = (typeof TECHNICAL_ROUTE_MAP)[keyof typeof TECHNICAL_ROUTE_MAP];
535
+ type RouteInfoFromPathInfo = {
536
+ routeName: RouteNameFromPathInfo;
537
+ foreignKey: string;
538
+ };
539
+ declare function normalizePath(path: string): string;
540
+ declare function isTechnicalPath(path: string): boolean;
541
+ declare function getRouteFromPathInfo(path: string): RouteInfoFromPathInfo | null;
542
+
525
543
  /**
526
544
  * Get translated property from the given object.
527
545
  *
@@ -616,7 +634,7 @@ declare function getMedia<T extends {
616
634
  }>(lineItem: T): ProductMedia[];
617
635
 
618
636
  /**
619
- * Returns the smallest thumbnail url from the media object
637
+ * Returns the smallest thumbnail url from the media object or the media.url if no thumbnails are available
620
638
  *
621
639
  * @param media image object
622
640
  *
@@ -628,6 +646,7 @@ declare function getSmallestThumbnailUrl<T extends {
628
646
  width: number;
629
647
  url: string;
630
648
  }>;
649
+ url?: string;
631
650
  }>(media?: T): string | undefined;
632
651
 
633
652
  /**
@@ -640,6 +659,41 @@ declare function getSmallestThumbnailUrl<T extends {
640
659
  * @category Media
641
660
  */
642
661
  declare function encodeUrlPath(urlString: string): string;
662
+ /**
663
+ * Generates a srcset string using CDN width-based resizing.
664
+ * Useful as a fallback when media has no pre-generated thumbnails.
665
+ *
666
+ * @param src - Base image URL
667
+ * @param widths - Array of widths to generate (default: [400, 800, 1200, 1600])
668
+ * @param options - Optional format and quality params
669
+ * @returns srcset string or undefined if src is invalid
670
+ *
671
+ * @public
672
+ * @category Media
673
+ */
674
+ declare function generateCdnSrcSet(src: string | undefined, widths?: number[], options?: {
675
+ format?: string;
676
+ quality?: number;
677
+ }): string | undefined;
678
+ /**
679
+ * Builds an optimized CDN image URL with size parameters.
680
+ * Adds width or height (whichever is larger, width if equal)
681
+ * rounded up to the nearest 100px.
682
+ *
683
+ * @param src - Base image URL
684
+ * @param dimensions - Rendered element dimensions (width and height)
685
+ * @returns Optimized image URL string
686
+ *
687
+ * @public
688
+ * @category Media
689
+ */
690
+ declare function buildCdnImageUrl(src: string | undefined, dimensions: {
691
+ width: number;
692
+ height: number;
693
+ }, options?: {
694
+ format?: string;
695
+ quality?: number;
696
+ }): string;
643
697
  /**
644
698
  * Returns the srcset attribute for the image, for available breakpoints
645
699
  *
@@ -715,4 +769,5 @@ declare function canUseQuoteActions<T extends {
715
769
  };
716
770
  }>(quote: T): boolean;
717
771
 
718
- export { type HelpersCssClasses, type LayoutConfiguration, type TierPrice, type UiMediaGalleryItem, type UiMediaGalleryItemUrl, type UiProductOption, type UiProductProperty, type UiProductReview, type UrlRouteOutput, buildUrlPrefix, canUseQuoteActions, downloadFile, encodeUrlPath, getBackgroundImageUrl, getBiggestThumbnailUrl, getCategoryBreadcrumbs, getCategoryImageUrl, getCategoryRoute, getCategoryUrl, getCmsBreadcrumbs, getCmsEntityObject, getCmsLayoutConfiguration, getCmsTranslate, getFormattedPrice, getLanguageName, getListingFilters, getMainImageUrl, getMedia, getPaymentMethodIcon, getProductCalculatedListingPrice, getProductFreeShipping, getProductFromPrice, getProductListingFromCmsPage, getProductManufacturerName, getProductName, getProductRatingAverage, getProductReviews, getProductRoute, getProductTierPrices, getProductUrl, getShippingMethodDeliveryTime, getShippingMethodIcon, getSmallestThumbnailUrl, getSrcSetForMedia, getTranslatedProperty, helpersCssClasses, isCategory, isLandingPage, isMaintenanceMode, isProduct, isProductOnSale, isProductTopSeller, relativeUrlSlash, urlIsAbsolute };
772
+ export { buildCdnImageUrl, buildUrlPrefix, canUseQuoteActions, downloadFile, encodeUrlPath, generateCdnSrcSet, getBackgroundImageUrl, getBiggestThumbnailUrl, getCategoryBreadcrumbs, getCategoryImageUrl, getCategoryRoute, getCategoryUrl, getCmsBreadcrumbs, getCmsEntityObject, getCmsLayoutConfiguration, getCmsTranslate, getFormattedPrice, getLanguageName, getListingFilters, getMainImageUrl, getMedia, getPaymentMethodIcon, getProductCalculatedListingPrice, getProductFreeShipping, getProductFromPrice, getProductListingFromCmsPage, getProductManufacturerName, getProductName, getProductRatingAverage, getProductReviews, getProductRoute, getProductTierPrices, getProductUrl, getRouteFromPathInfo, getShippingMethodDeliveryTime, getShippingMethodIcon, getSmallestThumbnailUrl, getSrcSetForMedia, getTranslatedProperty, helpersCssClasses, isCategory, isLandingPage, isMaintenanceMode, isProduct, isProductOnSale, isProductTopSeller, isTechnicalPath, normalizePath, relativeUrlSlash, urlIsAbsolute };
773
+ export type { BackgroundImageOptions, HelpersCssClasses, LayoutConfiguration, RouteInfoFromPathInfo, RouteNameFromPathInfo, TierPrice, UiMediaGalleryItem, UiMediaGalleryItemUrl, UiProductOption, UiProductProperty, UiProductReview, UrlRouteOutput };
package/dist/index.mjs CHANGED
@@ -260,12 +260,18 @@ const getUrlFromBackgroundImage = (url) => {
260
260
  const match = url.match(regex);
261
261
  return !match ? url : match[1];
262
262
  };
263
- function getBackgroundImageUrl(url, element) {
263
+ function getBackgroundImageUrl(url, element, options) {
264
264
  const backgroundImage = getUrlFromBackgroundImage(url);
265
265
  const width = element.backgroundMedia?.metaData?.width ?? 0;
266
266
  const height = element.backgroundMedia?.metaData?.height ?? 0;
267
267
  const biggestParam = width > height ? `width=${roundUp(width > 1920 ? 1900 : width)}` : `height=${roundUp(height > 1920 ? 1900 : height)}`;
268
- const srcPath = `${backgroundImage}?${biggestParam}&fit=crop,smart`;
268
+ let srcPath = `${backgroundImage}?${biggestParam}&fit=crop,smart`;
269
+ if (options?.format) {
270
+ srcPath += `&format=${options.format}`;
271
+ }
272
+ if (options?.quality) {
273
+ srcPath += `&quality=${options.quality}`;
274
+ }
269
275
  return `url("${srcPath}")`;
270
276
  }
271
277
 
@@ -346,6 +352,34 @@ function isLandingPage(entity) {
346
352
  return entity.apiAlias === "landing_page";
347
353
  }
348
354
 
355
+ const TECHNICAL_ROUTE_MAP = {
356
+ "/navigation/": "frontend.navigation.page",
357
+ "/detail/": "frontend.detail.page",
358
+ "/landingPage/": "frontend.landing.page"
359
+ };
360
+ function normalizePath(path) {
361
+ return path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
362
+ }
363
+ function isTechnicalPath(path) {
364
+ const normalizedPath = normalizePath(path);
365
+ return Object.keys(TECHNICAL_ROUTE_MAP).some(
366
+ (prefix) => normalizedPath.startsWith(prefix)
367
+ );
368
+ }
369
+ function getRouteFromPathInfo(path) {
370
+ const normalizedPath = normalizePath(path);
371
+ for (const [prefix, routeName] of Object.entries(TECHNICAL_ROUTE_MAP)) {
372
+ if (!normalizedPath.startsWith(prefix)) continue;
373
+ const foreignKey = normalizedPath.slice(prefix.length);
374
+ if (!foreignKey || foreignKey.includes("/")) return null;
375
+ return {
376
+ routeName,
377
+ foreignKey
378
+ };
379
+ }
380
+ return null;
381
+ }
382
+
349
383
  const getFilter = (code, aggregation) => {
350
384
  return {
351
385
  label: getTranslatedProperty(aggregation, "name") || code,
@@ -428,6 +462,9 @@ function getMedia(lineItem) {
428
462
 
429
463
  function getSmallestThumbnailUrl(media) {
430
464
  if (!media || !media?.thumbnails?.length) {
465
+ if (media?.url) {
466
+ return media.url;
467
+ }
431
468
  return;
432
469
  }
433
470
  const lowest = media.thumbnails.reduce((previous, current) => {
@@ -445,6 +482,51 @@ function encodeUrlPath(urlString) {
445
482
  return urlString;
446
483
  }
447
484
  }
485
+ function generateCdnSrcSet(src, widths = [400, 800, 1200, 1600], options) {
486
+ if (!src) return void 0;
487
+ try {
488
+ return widths.map((w) => {
489
+ const url = new URL(encodeUrlPath(src));
490
+ url.searchParams.set("width", String(w));
491
+ url.searchParams.set("fit", "crop,smart");
492
+ if (options?.format) {
493
+ url.searchParams.set("format", options.format);
494
+ }
495
+ if (options?.quality) {
496
+ url.searchParams.set("quality", String(options.quality));
497
+ }
498
+ return `${url.toString()} ${w}w`;
499
+ }).join(", ");
500
+ } catch {
501
+ return void 0;
502
+ }
503
+ }
504
+ function buildCdnImageUrl(src, dimensions, options) {
505
+ if (!src) return "";
506
+ try {
507
+ const url = new URL(encodeUrlPath(src));
508
+ const DEFAULT_SIZE = 10;
509
+ const w = dimensions.width ? Math.ceil(dimensions.width / 100) * 100 : DEFAULT_SIZE;
510
+ const h = dimensions.height ? Math.ceil(dimensions.height / 100) * 100 : DEFAULT_SIZE;
511
+ if (w > DEFAULT_SIZE || h > DEFAULT_SIZE) {
512
+ if (dimensions.width >= dimensions.height) {
513
+ url.searchParams.set("width", String(w));
514
+ } else {
515
+ url.searchParams.set("height", String(h));
516
+ }
517
+ url.searchParams.set("fit", "crop,smart");
518
+ }
519
+ if (options?.format) {
520
+ url.searchParams.set("format", options.format);
521
+ }
522
+ if (options?.quality) {
523
+ url.searchParams.set("quality", String(options.quality));
524
+ }
525
+ return url.toString();
526
+ } catch {
527
+ return src;
528
+ }
529
+ }
448
530
  function getSrcSetForMedia(media) {
449
531
  if (!media?.thumbnails?.length) {
450
532
  return;
@@ -482,4 +564,4 @@ function canUseQuoteActions(quote) {
482
564
  return ["replied"].includes(quote.stateMachineState?.technicalName ?? "");
483
565
  }
484
566
 
485
- export { buildUrlPrefix, canUseQuoteActions, downloadFile, encodeUrlPath, getBackgroundImageUrl, getBiggestThumbnailUrl, getCategoryBreadcrumbs, getCategoryImageUrl, getCategoryRoute, getCategoryUrl, getCmsBreadcrumbs, getCmsEntityObject, getCmsLayoutConfiguration, getCmsTranslate, getFormattedPrice, getLanguageName, getListingFilters, getMainImageUrl, getMedia, getPaymentMethodIcon, getProductCalculatedListingPrice, getProductFreeShipping, getProductFromPrice, getProductListingFromCmsPage, getProductManufacturerName, getProductName, getProductRatingAverage, getProductReviews, getProductRoute, getProductTierPrices, getProductUrl, getShippingMethodDeliveryTime, getShippingMethodIcon, getSmallestThumbnailUrl, getSrcSetForMedia, getTranslatedProperty, helpersCssClasses, isCategory, isLandingPage, isMaintenanceMode, isProduct, isProductOnSale, isProductTopSeller, relativeUrlSlash, urlIsAbsolute };
567
+ export { buildCdnImageUrl, buildUrlPrefix, canUseQuoteActions, downloadFile, encodeUrlPath, generateCdnSrcSet, getBackgroundImageUrl, getBiggestThumbnailUrl, getCategoryBreadcrumbs, getCategoryImageUrl, getCategoryRoute, getCategoryUrl, getCmsBreadcrumbs, getCmsEntityObject, getCmsLayoutConfiguration, getCmsTranslate, getFormattedPrice, getLanguageName, getListingFilters, getMainImageUrl, getMedia, getPaymentMethodIcon, getProductCalculatedListingPrice, getProductFreeShipping, getProductFromPrice, getProductListingFromCmsPage, getProductManufacturerName, getProductName, getProductRatingAverage, getProductReviews, getProductRoute, getProductTierPrices, getProductUrl, getRouteFromPathInfo, getShippingMethodDeliveryTime, getShippingMethodIcon, getSmallestThumbnailUrl, getSrcSetForMedia, getTranslatedProperty, helpersCssClasses, isCategory, isLandingPage, isMaintenanceMode, isProduct, isProductOnSale, isProductTopSeller, isTechnicalPath, normalizePath, relativeUrlSlash, urlIsAbsolute };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shopware/helpers",
3
- "version": "1.6.0",
3
+ "version": "1.7.1",
4
4
  "description": "Shopware helpers for accessing API data",
5
5
  "author": "Shopware",
6
6
  "type": "module",
@@ -33,19 +33,20 @@
33
33
  },
34
34
  "devDependencies": {
35
35
  "@biomejs/biome": "1.8.3",
36
- "@vitest/coverage-v8": "3.2.4",
37
- "happy-dom": "20.0.10",
36
+ "@typescript/native-preview": "7.0.0-dev.20260205.1",
37
+ "@vitest/coverage-v8": "4.1.5",
38
+ "happy-dom": "20.9.0",
38
39
  "unbuild": "2.0.0",
39
- "vitest": "3.2.4",
40
+ "vitest": "4.1.5",
40
41
  "tsconfig": "0.0.0"
41
42
  },
42
43
  "scripts": {
43
44
  "build": "unbuild && pnpm build:types",
44
- "build:types": "tsc ./src/*.ts --declaration --allowJs --emitDeclarationOnly --outDir ./temp --skipLibCheck",
45
+ "build:types": "tsgo ./src/*.ts --ignoreConfig --declaration --allowJs --emitDeclarationOnly --outDir ./temp --skipLibCheck",
45
46
  "dev": "unbuild --stub",
46
47
  "lint": "biome check .",
47
48
  "lint:fix": "biome check . --write && pnpm run typecheck",
48
- "typecheck": "tsc --noEmit",
49
+ "typecheck": "tsgo --noEmit",
49
50
  "test": "vitest run",
50
51
  "test:watch": "vitest"
51
52
  }