@shopware/helpers 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,14 +30,107 @@ export default defineConfig({
30
30
  });
31
31
  ```
32
32
 
33
+ ## `getBackgroundImageUrl` helper
34
+
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
+
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
+ ```
49
+
50
+ ### Parameters
51
+
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 |
57
+
58
+ ### `BackgroundImageOptions`
59
+
60
+ ```ts
61
+ type BackgroundImageOptions = {
62
+ format?: string; // "webp" | "avif" | "jpg" | "png"
63
+ quality?: number; // 0-100
64
+ };
65
+ ```
66
+
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.
95
+
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
+
33
122
  <!-- AUTO GENERATED CHANGELOG -->
34
123
 
35
124
  ## Changelog
36
125
 
37
126
  Full changelog for stable version is available [here](https://github.com/shopware/frontends/blob/main/packages/helpers/CHANGELOG.md)
38
127
 
39
- ### Latest changes: 1.5.0
128
+ ### Latest changes: 1.7.0
40
129
 
41
130
  ### Minor Changes
42
131
 
43
- - [#1910](https://github.com/shopware/frontends/pull/1910) [`c8fa438`](https://github.com/shopware/frontends/commit/c8fa438b38de6dbc43a2895f2d1906907447c384) Thanks [@mdanilowicz](https://github.com/mdanilowicz)! - Add helper for getting payment and shipping method icon
132
+ - [#2287](https://github.com/shopware/frontends/pull/2287) [`c9bde38`](https://github.com/shopware/frontends/commit/c9bde38d497d5c6c2fbd97700a362eb44ce8881f) Thanks [@mkucmus](https://github.com/mkucmus)! - Add generateCdnSrcSet and buildCdnImageUrl helpers for synthetic responsive image srcset generation via CDN query params
133
+
134
+ ### Patch Changes
135
+
136
+ - [#2320](https://github.com/shopware/frontends/pull/2320) [`a871c7b`](https://github.com/shopware/frontends/commit/a871c7b6256b75c2e40d93fc0354ba1971420062) Thanks [@mdanilowicz](https://github.com/mdanilowicz)! - Improve getSmallestThumbnailUrl - add media.url fallback when thumbnails are missing or empty
package/dist/index.d.mts CHANGED
@@ -22,7 +22,7 @@ declare function getProductFreeShipping<T extends {
22
22
  * @category Product
23
23
  */
24
24
  declare function getMainImageUrl<T extends {
25
- cover?: {
25
+ cover: {
26
26
  media?: {
27
27
  url: string;
28
28
  };
@@ -34,9 +34,11 @@ declare function getMainImageUrl<T extends {
34
34
  };
35
35
  }>;
36
36
  } | {
37
- cover?: {
37
+ cover: {
38
38
  url: string;
39
39
  };
40
+ } | {
41
+ cover: null;
40
42
  }>(object: T): string;
41
43
 
42
44
  /**
@@ -205,6 +207,18 @@ declare function getProductFromPrice<T extends {
205
207
  calculatedPrices?: CalculatedPrice[];
206
208
  }>(product: T): number | undefined;
207
209
 
210
+ interface ProductWithManufacturer {
211
+ manufacturer?: {
212
+ translated?: {
213
+ name?: string;
214
+ };
215
+ };
216
+ }
217
+ /**
218
+ * Gets the translated name of the product manufacturer
219
+ */
220
+ declare function getProductManufacturerName(product: ProductWithManufacturer): string;
221
+
208
222
  /**
209
223
  * Get product route information for Vue router.
210
224
  *
@@ -228,6 +242,24 @@ declare function getProductRoute<T extends {
228
242
  };
229
243
  };
230
244
 
245
+ /**
246
+ * Checks if a product is on sale based on its price percentage
247
+ */
248
+ declare function isProductOnSale(product: {
249
+ calculatedPrice: {
250
+ listPrice?: {
251
+ percentage?: number;
252
+ } | null;
253
+ };
254
+ }): boolean;
255
+
256
+ /**
257
+ * Checks if a product is marked as a top seller
258
+ */
259
+ declare function isProductTopSeller(product: {
260
+ markAsTopseller?: boolean;
261
+ }): boolean;
262
+
231
263
  /**
232
264
  * Get URL for category.
233
265
  * Some link {@link isLinkCategory}
@@ -365,6 +397,10 @@ type LayoutConfiguration = {
365
397
  */
366
398
  declare function getCmsLayoutConfiguration<T extends CmsBlock | CmsSection | CmsSlot>(content: T): LayoutConfiguration;
367
399
 
400
+ type BackgroundImageOptions = {
401
+ format?: string;
402
+ quality?: number;
403
+ };
368
404
  declare function getBackgroundImageUrl<T extends {
369
405
  backgroundMedia?: {
370
406
  metaData?: {
@@ -372,7 +408,7 @@ declare function getBackgroundImageUrl<T extends {
372
408
  height?: number;
373
409
  };
374
410
  };
375
- }>(url: string, element: T): string;
411
+ }>(url: string, element: T, options?: BackgroundImageOptions): string;
376
412
 
377
413
  type UrlRoute = {
378
414
  path?: string;
@@ -418,6 +454,35 @@ declare function getCmsBreadcrumbs<T extends {
418
454
  name: string;
419
455
  }[];
420
456
 
457
+ type CmsPageStructure = {
458
+ sections: Array<{
459
+ blocks?: Array<{
460
+ slots?: Array<{
461
+ type?: string;
462
+ data?: unknown;
463
+ [key: string]: unknown;
464
+ }>;
465
+ [key: string]: unknown;
466
+ }>;
467
+ [key: string]: unknown;
468
+ }>;
469
+ [key: string]: unknown;
470
+ };
471
+ /**
472
+ * Extracts the product listing data from a CMS page structure.
473
+ * Useful for SSR to get listing data early before components render.
474
+ *
475
+ * @param cmsPage - The CMS page object (Schemas["CmsPage"])
476
+ * @returns The product listing result or null if not found
477
+ *
478
+ * @example
479
+ * ```ts
480
+ * const listing = getProductListingFromCmsPage(cmsPage);
481
+ * createCategoryListingContext(listing);
482
+ * ```
483
+ */
484
+ declare function getProductListingFromCmsPage<T = unknown>(cmsPage: CmsPageStructure): T | null;
485
+
421
486
  type Category = {
422
487
  apiAlias: "category";
423
488
  };
@@ -475,6 +540,18 @@ type ListingFilter = {
475
540
  code: string;
476
541
  id: string;
477
542
  name: string;
543
+ options?: Array<{
544
+ id: string;
545
+ translated?: {
546
+ name?: string;
547
+ };
548
+ }>;
549
+ entities?: Array<{
550
+ id: string;
551
+ translated?: {
552
+ name?: string;
553
+ };
554
+ }>;
478
555
  };
479
556
  /**
480
557
  * @beta
@@ -543,7 +620,7 @@ declare function getMedia<T extends {
543
620
  }>(lineItem: T): ProductMedia[];
544
621
 
545
622
  /**
546
- * Returns the smallest thumbnail url from the media object
623
+ * Returns the smallest thumbnail url from the media object or the media.url if no thumbnails are available
547
624
  *
548
625
  * @param media image object
549
626
  *
@@ -555,8 +632,54 @@ declare function getSmallestThumbnailUrl<T extends {
555
632
  width: number;
556
633
  url: string;
557
634
  }>;
635
+ url?: string;
558
636
  }>(media?: T): string | undefined;
559
637
 
638
+ /**
639
+ * Encodes URL pathname to handle special characters (spaces, commas, etc.)
640
+ *
641
+ * @param urlString - The URL string to encode
642
+ * @returns The URL with encoded pathname
643
+ *
644
+ * @public
645
+ * @category Media
646
+ */
647
+ declare function encodeUrlPath(urlString: string): string;
648
+ /**
649
+ * Generates a srcset string using CDN width-based resizing.
650
+ * Useful as a fallback when media has no pre-generated thumbnails.
651
+ *
652
+ * @param src - Base image URL
653
+ * @param widths - Array of widths to generate (default: [400, 800, 1200, 1600])
654
+ * @param options - Optional format and quality params
655
+ * @returns srcset string or undefined if src is invalid
656
+ *
657
+ * @public
658
+ * @category Media
659
+ */
660
+ declare function generateCdnSrcSet(src: string | undefined, widths?: number[], options?: {
661
+ format?: string;
662
+ quality?: number;
663
+ }): string | undefined;
664
+ /**
665
+ * Builds an optimized CDN image URL with size parameters.
666
+ * Adds width or height (whichever is larger, width if equal)
667
+ * rounded up to the nearest 100px.
668
+ *
669
+ * @param src - Base image URL
670
+ * @param dimensions - Rendered element dimensions (width and height)
671
+ * @returns Optimized image URL string
672
+ *
673
+ * @public
674
+ * @category Media
675
+ */
676
+ declare function buildCdnImageUrl(src: string | undefined, dimensions: {
677
+ width: number;
678
+ height: number;
679
+ }, options?: {
680
+ format?: string;
681
+ quality?: number;
682
+ }): string;
560
683
  /**
561
684
  * Returns the srcset attribute for the image, for available breakpoints
562
685
  *
@@ -632,4 +755,4 @@ declare function canUseQuoteActions<T extends {
632
755
  };
633
756
  }>(quote: T): boolean;
634
757
 
635
- export { type HelpersCssClasses, type LayoutConfiguration, type TierPrice, type UiMediaGalleryItem, type UiMediaGalleryItemUrl, type UiProductOption, type UiProductProperty, type UiProductReview, buildUrlPrefix, canUseQuoteActions, downloadFile, getBackgroundImageUrl, getBiggestThumbnailUrl, getCategoryBreadcrumbs, getCategoryImageUrl, getCategoryRoute, getCategoryUrl, getCmsBreadcrumbs, getCmsEntityObject, getCmsLayoutConfiguration, getCmsTranslate, getFormattedPrice, getLanguageName, getListingFilters, getMainImageUrl, getMedia, getPaymentMethodIcon, getProductCalculatedListingPrice, getProductFreeShipping, getProductFromPrice, getProductName, getProductRatingAverage, getProductReviews, getProductRoute, getProductTierPrices, getProductUrl, getShippingMethodDeliveryTime, getShippingMethodIcon, getSmallestThumbnailUrl, getSrcSetForMedia, getTranslatedProperty, helpersCssClasses, isCategory, isLandingPage, isMaintenanceMode, isProduct, relativeUrlSlash, urlIsAbsolute };
758
+ export { type BackgroundImageOptions, type HelpersCssClasses, type LayoutConfiguration, type TierPrice, type UiMediaGalleryItem, type UiMediaGalleryItemUrl, type UiProductOption, type UiProductProperty, type UiProductReview, type UrlRouteOutput, 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, getShippingMethodDeliveryTime, getShippingMethodIcon, getSmallestThumbnailUrl, getSrcSetForMedia, getTranslatedProperty, helpersCssClasses, isCategory, isLandingPage, isMaintenanceMode, isProduct, isProductOnSale, isProductTopSeller, relativeUrlSlash, urlIsAbsolute };
package/dist/index.d.ts CHANGED
@@ -22,7 +22,7 @@ declare function getProductFreeShipping<T extends {
22
22
  * @category Product
23
23
  */
24
24
  declare function getMainImageUrl<T extends {
25
- cover?: {
25
+ cover: {
26
26
  media?: {
27
27
  url: string;
28
28
  };
@@ -34,9 +34,11 @@ declare function getMainImageUrl<T extends {
34
34
  };
35
35
  }>;
36
36
  } | {
37
- cover?: {
37
+ cover: {
38
38
  url: string;
39
39
  };
40
+ } | {
41
+ cover: null;
40
42
  }>(object: T): string;
41
43
 
42
44
  /**
@@ -205,6 +207,18 @@ declare function getProductFromPrice<T extends {
205
207
  calculatedPrices?: CalculatedPrice[];
206
208
  }>(product: T): number | undefined;
207
209
 
210
+ interface ProductWithManufacturer {
211
+ manufacturer?: {
212
+ translated?: {
213
+ name?: string;
214
+ };
215
+ };
216
+ }
217
+ /**
218
+ * Gets the translated name of the product manufacturer
219
+ */
220
+ declare function getProductManufacturerName(product: ProductWithManufacturer): string;
221
+
208
222
  /**
209
223
  * Get product route information for Vue router.
210
224
  *
@@ -228,6 +242,24 @@ declare function getProductRoute<T extends {
228
242
  };
229
243
  };
230
244
 
245
+ /**
246
+ * Checks if a product is on sale based on its price percentage
247
+ */
248
+ declare function isProductOnSale(product: {
249
+ calculatedPrice: {
250
+ listPrice?: {
251
+ percentage?: number;
252
+ } | null;
253
+ };
254
+ }): boolean;
255
+
256
+ /**
257
+ * Checks if a product is marked as a top seller
258
+ */
259
+ declare function isProductTopSeller(product: {
260
+ markAsTopseller?: boolean;
261
+ }): boolean;
262
+
231
263
  /**
232
264
  * Get URL for category.
233
265
  * Some link {@link isLinkCategory}
@@ -365,6 +397,10 @@ type LayoutConfiguration = {
365
397
  */
366
398
  declare function getCmsLayoutConfiguration<T extends CmsBlock | CmsSection | CmsSlot>(content: T): LayoutConfiguration;
367
399
 
400
+ type BackgroundImageOptions = {
401
+ format?: string;
402
+ quality?: number;
403
+ };
368
404
  declare function getBackgroundImageUrl<T extends {
369
405
  backgroundMedia?: {
370
406
  metaData?: {
@@ -372,7 +408,7 @@ declare function getBackgroundImageUrl<T extends {
372
408
  height?: number;
373
409
  };
374
410
  };
375
- }>(url: string, element: T): string;
411
+ }>(url: string, element: T, options?: BackgroundImageOptions): string;
376
412
 
377
413
  type UrlRoute = {
378
414
  path?: string;
@@ -418,6 +454,35 @@ declare function getCmsBreadcrumbs<T extends {
418
454
  name: string;
419
455
  }[];
420
456
 
457
+ type CmsPageStructure = {
458
+ sections: Array<{
459
+ blocks?: Array<{
460
+ slots?: Array<{
461
+ type?: string;
462
+ data?: unknown;
463
+ [key: string]: unknown;
464
+ }>;
465
+ [key: string]: unknown;
466
+ }>;
467
+ [key: string]: unknown;
468
+ }>;
469
+ [key: string]: unknown;
470
+ };
471
+ /**
472
+ * Extracts the product listing data from a CMS page structure.
473
+ * Useful for SSR to get listing data early before components render.
474
+ *
475
+ * @param cmsPage - The CMS page object (Schemas["CmsPage"])
476
+ * @returns The product listing result or null if not found
477
+ *
478
+ * @example
479
+ * ```ts
480
+ * const listing = getProductListingFromCmsPage(cmsPage);
481
+ * createCategoryListingContext(listing);
482
+ * ```
483
+ */
484
+ declare function getProductListingFromCmsPage<T = unknown>(cmsPage: CmsPageStructure): T | null;
485
+
421
486
  type Category = {
422
487
  apiAlias: "category";
423
488
  };
@@ -475,6 +540,18 @@ type ListingFilter = {
475
540
  code: string;
476
541
  id: string;
477
542
  name: string;
543
+ options?: Array<{
544
+ id: string;
545
+ translated?: {
546
+ name?: string;
547
+ };
548
+ }>;
549
+ entities?: Array<{
550
+ id: string;
551
+ translated?: {
552
+ name?: string;
553
+ };
554
+ }>;
478
555
  };
479
556
  /**
480
557
  * @beta
@@ -543,7 +620,7 @@ declare function getMedia<T extends {
543
620
  }>(lineItem: T): ProductMedia[];
544
621
 
545
622
  /**
546
- * Returns the smallest thumbnail url from the media object
623
+ * Returns the smallest thumbnail url from the media object or the media.url if no thumbnails are available
547
624
  *
548
625
  * @param media image object
549
626
  *
@@ -555,8 +632,54 @@ declare function getSmallestThumbnailUrl<T extends {
555
632
  width: number;
556
633
  url: string;
557
634
  }>;
635
+ url?: string;
558
636
  }>(media?: T): string | undefined;
559
637
 
638
+ /**
639
+ * Encodes URL pathname to handle special characters (spaces, commas, etc.)
640
+ *
641
+ * @param urlString - The URL string to encode
642
+ * @returns The URL with encoded pathname
643
+ *
644
+ * @public
645
+ * @category Media
646
+ */
647
+ declare function encodeUrlPath(urlString: string): string;
648
+ /**
649
+ * Generates a srcset string using CDN width-based resizing.
650
+ * Useful as a fallback when media has no pre-generated thumbnails.
651
+ *
652
+ * @param src - Base image URL
653
+ * @param widths - Array of widths to generate (default: [400, 800, 1200, 1600])
654
+ * @param options - Optional format and quality params
655
+ * @returns srcset string or undefined if src is invalid
656
+ *
657
+ * @public
658
+ * @category Media
659
+ */
660
+ declare function generateCdnSrcSet(src: string | undefined, widths?: number[], options?: {
661
+ format?: string;
662
+ quality?: number;
663
+ }): string | undefined;
664
+ /**
665
+ * Builds an optimized CDN image URL with size parameters.
666
+ * Adds width or height (whichever is larger, width if equal)
667
+ * rounded up to the nearest 100px.
668
+ *
669
+ * @param src - Base image URL
670
+ * @param dimensions - Rendered element dimensions (width and height)
671
+ * @returns Optimized image URL string
672
+ *
673
+ * @public
674
+ * @category Media
675
+ */
676
+ declare function buildCdnImageUrl(src: string | undefined, dimensions: {
677
+ width: number;
678
+ height: number;
679
+ }, options?: {
680
+ format?: string;
681
+ quality?: number;
682
+ }): string;
560
683
  /**
561
684
  * Returns the srcset attribute for the image, for available breakpoints
562
685
  *
@@ -632,4 +755,4 @@ declare function canUseQuoteActions<T extends {
632
755
  };
633
756
  }>(quote: T): boolean;
634
757
 
635
- export { type HelpersCssClasses, type LayoutConfiguration, type TierPrice, type UiMediaGalleryItem, type UiMediaGalleryItemUrl, type UiProductOption, type UiProductProperty, type UiProductReview, buildUrlPrefix, canUseQuoteActions, downloadFile, getBackgroundImageUrl, getBiggestThumbnailUrl, getCategoryBreadcrumbs, getCategoryImageUrl, getCategoryRoute, getCategoryUrl, getCmsBreadcrumbs, getCmsEntityObject, getCmsLayoutConfiguration, getCmsTranslate, getFormattedPrice, getLanguageName, getListingFilters, getMainImageUrl, getMedia, getPaymentMethodIcon, getProductCalculatedListingPrice, getProductFreeShipping, getProductFromPrice, getProductName, getProductRatingAverage, getProductReviews, getProductRoute, getProductTierPrices, getProductUrl, getShippingMethodDeliveryTime, getShippingMethodIcon, getSmallestThumbnailUrl, getSrcSetForMedia, getTranslatedProperty, helpersCssClasses, isCategory, isLandingPage, isMaintenanceMode, isProduct, relativeUrlSlash, urlIsAbsolute };
758
+ export { type BackgroundImageOptions, type HelpersCssClasses, type LayoutConfiguration, type TierPrice, type UiMediaGalleryItem, type UiMediaGalleryItemUrl, type UiProductOption, type UiProductProperty, type UiProductReview, type UrlRouteOutput, 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, getShippingMethodDeliveryTime, getShippingMethodIcon, getSmallestThumbnailUrl, getSrcSetForMedia, getTranslatedProperty, helpersCssClasses, isCategory, isLandingPage, isMaintenanceMode, isProduct, isProductOnSale, isProductTopSeller, relativeUrlSlash, urlIsAbsolute };
package/dist/index.mjs CHANGED
@@ -91,6 +91,10 @@ function getProductFromPrice(product) {
91
91
  return void 0;
92
92
  }
93
93
 
94
+ function getProductManufacturerName(product) {
95
+ return product.manufacturer?.translated?.name || "";
96
+ }
97
+
94
98
  function getProductRoute(product) {
95
99
  return {
96
100
  path: getProductUrl(product),
@@ -101,6 +105,14 @@ function getProductRoute(product) {
101
105
  };
102
106
  }
103
107
 
108
+ function isProductOnSale(product) {
109
+ return (product.calculatedPrice?.listPrice?.percentage ?? 0) > 0;
110
+ }
111
+
112
+ function isProductTopSeller(product) {
113
+ return !!product.markAsTopseller;
114
+ }
115
+
104
116
  function getEntityPrefix(category) {
105
117
  switch (category.linkType) {
106
118
  case "category":
@@ -248,12 +260,18 @@ const getUrlFromBackgroundImage = (url) => {
248
260
  const match = url.match(regex);
249
261
  return !match ? url : match[1];
250
262
  };
251
- function getBackgroundImageUrl(url, element) {
263
+ function getBackgroundImageUrl(url, element, options) {
252
264
  const backgroundImage = getUrlFromBackgroundImage(url);
253
265
  const width = element.backgroundMedia?.metaData?.width ?? 0;
254
266
  const height = element.backgroundMedia?.metaData?.height ?? 0;
255
267
  const biggestParam = width > height ? `width=${roundUp(width > 1920 ? 1900 : width)}` : `height=${roundUp(height > 1920 ? 1900 : height)}`;
256
- 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
+ }
257
275
  return `url("${srcPath}")`;
258
276
  }
259
277
 
@@ -308,6 +326,12 @@ function getCmsBreadcrumbs(page) {
308
326
  ];
309
327
  }
310
328
 
329
+ function getProductListingFromCmsPage(cmsPage) {
330
+ const slot = cmsPage.sections.flatMap((section) => section.blocks ?? []).flatMap((block) => block.slots ?? []).find((slot2) => slot2.type === "product-listing");
331
+ const listing = slot?.data?.listing;
332
+ return listing ?? null;
333
+ }
334
+
311
335
  function getCmsEntityObject(response) {
312
336
  switch (response.resourceType) {
313
337
  case "frontend.detail.page":
@@ -410,6 +434,9 @@ function getMedia(lineItem) {
410
434
 
411
435
  function getSmallestThumbnailUrl(media) {
412
436
  if (!media || !media?.thumbnails?.length) {
437
+ if (media?.url) {
438
+ return media.url;
439
+ }
413
440
  return;
414
441
  }
415
442
  const lowest = media.thumbnails.reduce((previous, current) => {
@@ -418,12 +445,66 @@ function getSmallestThumbnailUrl(media) {
418
445
  return lowest?.url;
419
446
  }
420
447
 
448
+ function encodeUrlPath(urlString) {
449
+ try {
450
+ const url = new URL(urlString);
451
+ url.pathname = url.pathname.split("/").map((segment) => encodeURIComponent(decodeURIComponent(segment))).join("/");
452
+ return url.toString();
453
+ } catch {
454
+ return urlString;
455
+ }
456
+ }
457
+ function generateCdnSrcSet(src, widths = [400, 800, 1200, 1600], options) {
458
+ if (!src) return void 0;
459
+ try {
460
+ return widths.map((w) => {
461
+ const url = new URL(encodeUrlPath(src));
462
+ url.searchParams.set("width", String(w));
463
+ url.searchParams.set("fit", "crop,smart");
464
+ if (options?.format) {
465
+ url.searchParams.set("format", options.format);
466
+ }
467
+ if (options?.quality) {
468
+ url.searchParams.set("quality", String(options.quality));
469
+ }
470
+ return `${url.toString()} ${w}w`;
471
+ }).join(", ");
472
+ } catch {
473
+ return void 0;
474
+ }
475
+ }
476
+ function buildCdnImageUrl(src, dimensions, options) {
477
+ if (!src) return "";
478
+ try {
479
+ const url = new URL(encodeUrlPath(src));
480
+ const DEFAULT_SIZE = 10;
481
+ const w = dimensions.width ? Math.ceil(dimensions.width / 100) * 100 : DEFAULT_SIZE;
482
+ const h = dimensions.height ? Math.ceil(dimensions.height / 100) * 100 : DEFAULT_SIZE;
483
+ if (w > DEFAULT_SIZE || h > DEFAULT_SIZE) {
484
+ if (dimensions.width >= dimensions.height) {
485
+ url.searchParams.set("width", String(w));
486
+ } else {
487
+ url.searchParams.set("height", String(h));
488
+ }
489
+ url.searchParams.set("fit", "crop,smart");
490
+ }
491
+ if (options?.format) {
492
+ url.searchParams.set("format", options.format);
493
+ }
494
+ if (options?.quality) {
495
+ url.searchParams.set("quality", String(options.quality));
496
+ }
497
+ return url.toString();
498
+ } catch {
499
+ return src;
500
+ }
501
+ }
421
502
  function getSrcSetForMedia(media) {
422
503
  if (!media?.thumbnails?.length) {
423
504
  return;
424
505
  }
425
506
  return media.thumbnails.map((thumbnail) => {
426
- return thumbnail.url ? `${thumbnail.url} ${thumbnail.width}w` : void 0;
507
+ return thumbnail.url ? `${encodeUrlPath(thumbnail.url)} ${thumbnail.width}w` : void 0;
427
508
  }).filter((value) => !!value).join(", ");
428
509
  }
429
510
 
@@ -455,4 +536,4 @@ function canUseQuoteActions(quote) {
455
536
  return ["replied"].includes(quote.stateMachineState?.technicalName ?? "");
456
537
  }
457
538
 
458
- export { buildUrlPrefix, canUseQuoteActions, downloadFile, getBackgroundImageUrl, getBiggestThumbnailUrl, getCategoryBreadcrumbs, getCategoryImageUrl, getCategoryRoute, getCategoryUrl, getCmsBreadcrumbs, getCmsEntityObject, getCmsLayoutConfiguration, getCmsTranslate, getFormattedPrice, getLanguageName, getListingFilters, getMainImageUrl, getMedia, getPaymentMethodIcon, getProductCalculatedListingPrice, getProductFreeShipping, getProductFromPrice, getProductName, getProductRatingAverage, getProductReviews, getProductRoute, getProductTierPrices, getProductUrl, getShippingMethodDeliveryTime, getShippingMethodIcon, getSmallestThumbnailUrl, getSrcSetForMedia, getTranslatedProperty, helpersCssClasses, isCategory, isLandingPage, isMaintenanceMode, isProduct, relativeUrlSlash, urlIsAbsolute };
539
+ 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, getShippingMethodDeliveryTime, getShippingMethodIcon, getSmallestThumbnailUrl, getSrcSetForMedia, getTranslatedProperty, helpersCssClasses, isCategory, isLandingPage, isMaintenanceMode, isProduct, isProductOnSale, isProductTopSeller, relativeUrlSlash, urlIsAbsolute };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shopware/helpers",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
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.3",
37
- "happy-dom": "18.0.1",
36
+ "@typescript/native-preview": "7.0.0-dev.20260111.1",
37
+ "@vitest/coverage-v8": "4.0.18",
38
+ "happy-dom": "20.8.9",
38
39
  "unbuild": "2.0.0",
39
- "vitest": "3.2.3",
40
+ "vitest": "4.0.18",
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
  }