@shopware/helpers 1.7.1 → 1.8.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 +29 -28
- package/dist/index.d.mts +71 -3
- package/dist/index.d.ts +71 -3
- package/dist/index.mjs +93 -2
- package/package.json +8 -9
package/README.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Welcome to `@shopware/helpers` package.
|
|
4
4
|
|
|
5
|
-
For getting started documentation visit [https://
|
|
5
|
+
For getting started documentation visit [https://developer.shopware.com/frontends/](https://developer.shopware.com/frontends/)
|
|
6
6
|
|
|
7
|
-
Documentation specific for this package: [helpers](https://
|
|
7
|
+
Documentation specific for this package: [helpers](https://developer.shopware.com/frontends/packages/helpers.html)
|
|
8
8
|
|
|
9
9
|
## Reusable classes
|
|
10
10
|
|
|
@@ -49,11 +49,11 @@ const optimizedUrl = getBackgroundImageUrl(
|
|
|
49
49
|
|
|
50
50
|
### Parameters
|
|
51
51
|
|
|
52
|
-
| Parameter | Type
|
|
53
|
-
|
|
54
|
-
| `url`
|
|
55
|
-
| `element` | `{ backgroundMedia?: { metaData?: { width?: number; height?: number } } }` | CMS section or block object with media metadata
|
|
56
|
-
| `options` | `BackgroundImageOptions` (optional)
|
|
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
57
|
|
|
58
58
|
### `BackgroundImageOptions`
|
|
59
59
|
|
|
@@ -85,11 +85,11 @@ const srcset = generateCdnSrcSet(
|
|
|
85
85
|
|
|
86
86
|
### Parameters
|
|
87
87
|
|
|
88
|
-
| Parameter | Type
|
|
89
|
-
|
|
90
|
-
| `src`
|
|
91
|
-
| `widths`
|
|
92
|
-
| `options` | `{ format?: string; quality?: number }` (optional) | Format and quality settings
|
|
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
93
|
|
|
94
94
|
Returns `undefined` if `src` is falsy or URL parsing fails.
|
|
95
95
|
|
|
@@ -102,20 +102,20 @@ Builds an optimized CDN image URL with size parameters based on rendered element
|
|
|
102
102
|
```ts
|
|
103
103
|
import { buildCdnImageUrl } from "@shopware/helpers";
|
|
104
104
|
|
|
105
|
-
const url = buildCdnImageUrl(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
);
|
|
105
|
+
const url = buildCdnImageUrl("https://cdn.shopware.store/.../image.jpg", {
|
|
106
|
+
width: 724,
|
|
107
|
+
height: 760,
|
|
108
|
+
});
|
|
109
109
|
// => "https://cdn.shopware.store/.../image.jpg?height=800&fit=crop,smart"
|
|
110
110
|
```
|
|
111
111
|
|
|
112
112
|
### Parameters
|
|
113
113
|
|
|
114
|
-
| Parameter
|
|
115
|
-
|
|
116
|
-
| `src`
|
|
117
|
-
| `dimensions` | `{ width: number; height: number }`
|
|
118
|
-
| `options`
|
|
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
119
|
|
|
120
120
|
Returns an empty string if `src` is falsy. Returns the original `src` if URL parsing fails.
|
|
121
121
|
|
|
@@ -125,13 +125,14 @@ Returns an empty string if `src` is falsy. Returns the original `src` if URL par
|
|
|
125
125
|
|
|
126
126
|
Full changelog for stable version is available [here](https://github.com/shopware/frontends/blob/main/packages/helpers/CHANGELOG.md)
|
|
127
127
|
|
|
128
|
-
### Latest changes: 1.
|
|
128
|
+
### Latest changes: 1.8.0
|
|
129
129
|
|
|
130
|
-
###
|
|
130
|
+
### Minor Changes
|
|
131
131
|
|
|
132
|
-
- [#
|
|
132
|
+
- [#2574](https://github.com/shopware/frontends/pull/2574) [`2ddf156`](https://github.com/shopware/frontends/commit/2ddf156805b2941fe2069e78453fb3c4eb6d44ac) Thanks [@mkucmus](https://github.com/mkucmus)! - Add `getCategoryFilterAggregations()` and `getCategoryFilterPostFilter()` to request category aggregations for product listings and filter by category without reducing the aggregations. `excludeRootCategory()` drops the sales channel entry point from the category entities, and the `CATEGORY_AGGREGATION_NAME` / `CATEGORY_COUNTS_AGGREGATION_NAME` / `CATEGORY_PARENTS_AGGREGATION_NAME` constants are exported for consumers that build the aggregations themselves.
|
|
133
|
+
|
|
134
|
+
`getListingFilters` (`@beta`) merges the `categories` and `categories-counts` response aggregations into a single `categories` filter with a product count per category. This changes the shape of that filter for listings that already requested a `categories` aggregation: the entities are sorted by count (highest first) instead of keeping the response order, each entity gains a `count`, the filter no longer carries the aggregation's `apiAlias`, and `categories-counts` is no longer returned as a filter of its own.
|
|
135
|
+
|
|
136
|
+
### Patch Changes
|
|
133
137
|
|
|
134
|
-
|
|
135
|
-
`useNavigationSearch` to resolve `/navigation/*`, `/detail/*`, and
|
|
136
|
-
`/landingPage/*` routes more reliably, including fallback behavior when no SEO
|
|
137
|
-
mapping row is returned.
|
|
138
|
+
- [#2598](https://github.com/shopware/frontends/pull/2598) [`204c8f4`](https://github.com/shopware/frontends/commit/204c8f45f737e724db6d00b80c5faef8ddb77cb4) Thanks [@dependabot](https://github.com/apps/dependabot)! - Fix Nuxt plugin injection typing for Nuxt 4.5 and maintenance mode error handling.
|
package/dist/index.d.mts
CHANGED
|
@@ -427,7 +427,7 @@ type HelpersCssClasses = (typeof helpersCssClasses)[number];
|
|
|
427
427
|
|
|
428
428
|
declare function isMaintenanceMode<T extends {
|
|
429
429
|
code?: string;
|
|
430
|
-
}>(errors: [
|
|
430
|
+
}>(errors: T[]): boolean;
|
|
431
431
|
|
|
432
432
|
/**
|
|
433
433
|
* Replace text placeholder with param value
|
|
@@ -538,7 +538,19 @@ type RouteInfoFromPathInfo = {
|
|
|
538
538
|
};
|
|
539
539
|
declare function normalizePath(path: string): string;
|
|
540
540
|
declare function isTechnicalPath(path: string): boolean;
|
|
541
|
+
/**
|
|
542
|
+
* Check whether an absolute or relative URL points to a technical Shopware
|
|
543
|
+
* route. Query parameters and fragments are ignored.
|
|
544
|
+
*/
|
|
545
|
+
declare function isTechnicalUrl(url: string, baseUrl?: string): boolean;
|
|
541
546
|
declare function getRouteFromPathInfo(path: string): RouteInfoFromPathInfo | null;
|
|
547
|
+
/**
|
|
548
|
+
* Get the canonical path for a mapped technical Shopware URL.
|
|
549
|
+
*
|
|
550
|
+
* Returns `null` for SEO URLs, synthetic technical fallbacks without an SEO
|
|
551
|
+
* mapping, and invalid mappings that would redirect to another technical URL.
|
|
552
|
+
*/
|
|
553
|
+
declare function getCanonicalPathForTechnicalPath(path: string, seoUrl?: object | null): string | null;
|
|
542
554
|
|
|
543
555
|
/**
|
|
544
556
|
* Get translated property from the given object.
|
|
@@ -549,6 +561,61 @@ declare function getRouteFromPathInfo(path: string): RouteInfoFromPathInfo | nul
|
|
|
549
561
|
*/
|
|
550
562
|
declare function getTranslatedProperty<T>(element: T | undefined | null | never, property: keyof T): string;
|
|
551
563
|
|
|
564
|
+
declare const CATEGORY_AGGREGATION_NAME = "categories";
|
|
565
|
+
declare const CATEGORY_COUNTS_AGGREGATION_NAME = "categories-counts";
|
|
566
|
+
declare const CATEGORY_PARENTS_AGGREGATION_NAME = "categories-parents";
|
|
567
|
+
type CategoryEntityAggregation = {
|
|
568
|
+
name: string;
|
|
569
|
+
type: "entity";
|
|
570
|
+
definition: "category";
|
|
571
|
+
field: string;
|
|
572
|
+
};
|
|
573
|
+
type CategoryCountsAggregation = {
|
|
574
|
+
name: string;
|
|
575
|
+
type: "terms";
|
|
576
|
+
field: string;
|
|
577
|
+
};
|
|
578
|
+
type CategoryFilterBucket = {
|
|
579
|
+
key: string;
|
|
580
|
+
count: number;
|
|
581
|
+
} & {
|
|
582
|
+
[nestedAggregationName: string]: unknown;
|
|
583
|
+
};
|
|
584
|
+
/**
|
|
585
|
+
* Category aggregations for a listing filter: an entity `categories` agg plus a
|
|
586
|
+
* flat `categories-counts` terms agg. Kept flat because the ES search route
|
|
587
|
+
* returns no buckets once a nested sub-agg is attached, so variants overcount.
|
|
588
|
+
*
|
|
589
|
+
* @beta
|
|
590
|
+
*/
|
|
591
|
+
declare function getCategoryFilterAggregations(): Array<CategoryEntityAggregation | CategoryCountsAggregation>;
|
|
592
|
+
/**
|
|
593
|
+
* Category filter entities without the sales channel entry point.
|
|
594
|
+
*
|
|
595
|
+
* That root category is an ancestor of every product's category tree, so it
|
|
596
|
+
* matches the whole result set and filtering by it changes nothing. Opt in per
|
|
597
|
+
* listing by passing `sessionContext.salesChannel.navigationCategoryId`;
|
|
598
|
+
* without an id the entities are returned unchanged.
|
|
599
|
+
*
|
|
600
|
+
* @beta
|
|
601
|
+
*/
|
|
602
|
+
declare function excludeRootCategory<T extends {
|
|
603
|
+
id: string;
|
|
604
|
+
}>(entities: T[] | undefined | null, rootCategoryId: string | undefined | null): T[];
|
|
605
|
+
/**
|
|
606
|
+
* Criteria `post-filter` entry narrowing a product listing to the given
|
|
607
|
+
* category ids. Sent as a post-filter, it does not reduce the category
|
|
608
|
+
* aggregations from `getCategoryFilterAggregations`, so all category options
|
|
609
|
+
* stay visible while results are filtered (standard faceted behavior).
|
|
610
|
+
*
|
|
611
|
+
* @beta
|
|
612
|
+
*/
|
|
613
|
+
declare function getCategoryFilterPostFilter(categoryIds: string[]): {
|
|
614
|
+
field: string;
|
|
615
|
+
type: "equalsAny";
|
|
616
|
+
value: string;
|
|
617
|
+
};
|
|
618
|
+
|
|
552
619
|
type ListingFilter = {
|
|
553
620
|
label: string;
|
|
554
621
|
code: string;
|
|
@@ -565,6 +632,7 @@ type ListingFilter = {
|
|
|
565
632
|
translated?: {
|
|
566
633
|
name?: string;
|
|
567
634
|
};
|
|
635
|
+
count?: number;
|
|
568
636
|
}>;
|
|
569
637
|
};
|
|
570
638
|
/**
|
|
@@ -769,5 +837,5 @@ declare function canUseQuoteActions<T extends {
|
|
|
769
837
|
};
|
|
770
838
|
}>(quote: T): boolean;
|
|
771
839
|
|
|
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 };
|
|
840
|
+
export { CATEGORY_AGGREGATION_NAME, CATEGORY_COUNTS_AGGREGATION_NAME, CATEGORY_PARENTS_AGGREGATION_NAME, buildCdnImageUrl, buildUrlPrefix, canUseQuoteActions, downloadFile, encodeUrlPath, excludeRootCategory, generateCdnSrcSet, getBackgroundImageUrl, getBiggestThumbnailUrl, getCanonicalPathForTechnicalPath, getCategoryBreadcrumbs, getCategoryFilterAggregations, getCategoryFilterPostFilter, 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, isTechnicalUrl, normalizePath, relativeUrlSlash, urlIsAbsolute };
|
|
841
|
+
export type { BackgroundImageOptions, CategoryFilterBucket, HelpersCssClasses, LayoutConfiguration, RouteInfoFromPathInfo, RouteNameFromPathInfo, TierPrice, UiMediaGalleryItem, UiMediaGalleryItemUrl, UiProductOption, UiProductProperty, UiProductReview, UrlRouteOutput };
|
package/dist/index.d.ts
CHANGED
|
@@ -427,7 +427,7 @@ type HelpersCssClasses = (typeof helpersCssClasses)[number];
|
|
|
427
427
|
|
|
428
428
|
declare function isMaintenanceMode<T extends {
|
|
429
429
|
code?: string;
|
|
430
|
-
}>(errors: [
|
|
430
|
+
}>(errors: T[]): boolean;
|
|
431
431
|
|
|
432
432
|
/**
|
|
433
433
|
* Replace text placeholder with param value
|
|
@@ -538,7 +538,19 @@ type RouteInfoFromPathInfo = {
|
|
|
538
538
|
};
|
|
539
539
|
declare function normalizePath(path: string): string;
|
|
540
540
|
declare function isTechnicalPath(path: string): boolean;
|
|
541
|
+
/**
|
|
542
|
+
* Check whether an absolute or relative URL points to a technical Shopware
|
|
543
|
+
* route. Query parameters and fragments are ignored.
|
|
544
|
+
*/
|
|
545
|
+
declare function isTechnicalUrl(url: string, baseUrl?: string): boolean;
|
|
541
546
|
declare function getRouteFromPathInfo(path: string): RouteInfoFromPathInfo | null;
|
|
547
|
+
/**
|
|
548
|
+
* Get the canonical path for a mapped technical Shopware URL.
|
|
549
|
+
*
|
|
550
|
+
* Returns `null` for SEO URLs, synthetic technical fallbacks without an SEO
|
|
551
|
+
* mapping, and invalid mappings that would redirect to another technical URL.
|
|
552
|
+
*/
|
|
553
|
+
declare function getCanonicalPathForTechnicalPath(path: string, seoUrl?: object | null): string | null;
|
|
542
554
|
|
|
543
555
|
/**
|
|
544
556
|
* Get translated property from the given object.
|
|
@@ -549,6 +561,61 @@ declare function getRouteFromPathInfo(path: string): RouteInfoFromPathInfo | nul
|
|
|
549
561
|
*/
|
|
550
562
|
declare function getTranslatedProperty<T>(element: T | undefined | null | never, property: keyof T): string;
|
|
551
563
|
|
|
564
|
+
declare const CATEGORY_AGGREGATION_NAME = "categories";
|
|
565
|
+
declare const CATEGORY_COUNTS_AGGREGATION_NAME = "categories-counts";
|
|
566
|
+
declare const CATEGORY_PARENTS_AGGREGATION_NAME = "categories-parents";
|
|
567
|
+
type CategoryEntityAggregation = {
|
|
568
|
+
name: string;
|
|
569
|
+
type: "entity";
|
|
570
|
+
definition: "category";
|
|
571
|
+
field: string;
|
|
572
|
+
};
|
|
573
|
+
type CategoryCountsAggregation = {
|
|
574
|
+
name: string;
|
|
575
|
+
type: "terms";
|
|
576
|
+
field: string;
|
|
577
|
+
};
|
|
578
|
+
type CategoryFilterBucket = {
|
|
579
|
+
key: string;
|
|
580
|
+
count: number;
|
|
581
|
+
} & {
|
|
582
|
+
[nestedAggregationName: string]: unknown;
|
|
583
|
+
};
|
|
584
|
+
/**
|
|
585
|
+
* Category aggregations for a listing filter: an entity `categories` agg plus a
|
|
586
|
+
* flat `categories-counts` terms agg. Kept flat because the ES search route
|
|
587
|
+
* returns no buckets once a nested sub-agg is attached, so variants overcount.
|
|
588
|
+
*
|
|
589
|
+
* @beta
|
|
590
|
+
*/
|
|
591
|
+
declare function getCategoryFilterAggregations(): Array<CategoryEntityAggregation | CategoryCountsAggregation>;
|
|
592
|
+
/**
|
|
593
|
+
* Category filter entities without the sales channel entry point.
|
|
594
|
+
*
|
|
595
|
+
* That root category is an ancestor of every product's category tree, so it
|
|
596
|
+
* matches the whole result set and filtering by it changes nothing. Opt in per
|
|
597
|
+
* listing by passing `sessionContext.salesChannel.navigationCategoryId`;
|
|
598
|
+
* without an id the entities are returned unchanged.
|
|
599
|
+
*
|
|
600
|
+
* @beta
|
|
601
|
+
*/
|
|
602
|
+
declare function excludeRootCategory<T extends {
|
|
603
|
+
id: string;
|
|
604
|
+
}>(entities: T[] | undefined | null, rootCategoryId: string | undefined | null): T[];
|
|
605
|
+
/**
|
|
606
|
+
* Criteria `post-filter` entry narrowing a product listing to the given
|
|
607
|
+
* category ids. Sent as a post-filter, it does not reduce the category
|
|
608
|
+
* aggregations from `getCategoryFilterAggregations`, so all category options
|
|
609
|
+
* stay visible while results are filtered (standard faceted behavior).
|
|
610
|
+
*
|
|
611
|
+
* @beta
|
|
612
|
+
*/
|
|
613
|
+
declare function getCategoryFilterPostFilter(categoryIds: string[]): {
|
|
614
|
+
field: string;
|
|
615
|
+
type: "equalsAny";
|
|
616
|
+
value: string;
|
|
617
|
+
};
|
|
618
|
+
|
|
552
619
|
type ListingFilter = {
|
|
553
620
|
label: string;
|
|
554
621
|
code: string;
|
|
@@ -565,6 +632,7 @@ type ListingFilter = {
|
|
|
565
632
|
translated?: {
|
|
566
633
|
name?: string;
|
|
567
634
|
};
|
|
635
|
+
count?: number;
|
|
568
636
|
}>;
|
|
569
637
|
};
|
|
570
638
|
/**
|
|
@@ -769,5 +837,5 @@ declare function canUseQuoteActions<T extends {
|
|
|
769
837
|
};
|
|
770
838
|
}>(quote: T): boolean;
|
|
771
839
|
|
|
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 };
|
|
840
|
+
export { CATEGORY_AGGREGATION_NAME, CATEGORY_COUNTS_AGGREGATION_NAME, CATEGORY_PARENTS_AGGREGATION_NAME, buildCdnImageUrl, buildUrlPrefix, canUseQuoteActions, downloadFile, encodeUrlPath, excludeRootCategory, generateCdnSrcSet, getBackgroundImageUrl, getBiggestThumbnailUrl, getCanonicalPathForTechnicalPath, getCategoryBreadcrumbs, getCategoryFilterAggregations, getCategoryFilterPostFilter, 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, isTechnicalUrl, normalizePath, relativeUrlSlash, urlIsAbsolute };
|
|
841
|
+
export type { BackgroundImageOptions, CategoryFilterBucket, HelpersCssClasses, LayoutConfiguration, RouteInfoFromPathInfo, RouteNameFromPathInfo, TierPrice, UiMediaGalleryItem, UiMediaGalleryItemUrl, UiProductOption, UiProductProperty, UiProductReview, UrlRouteOutput };
|
package/dist/index.mjs
CHANGED
|
@@ -366,6 +366,13 @@ function isTechnicalPath(path) {
|
|
|
366
366
|
(prefix) => normalizedPath.startsWith(prefix)
|
|
367
367
|
);
|
|
368
368
|
}
|
|
369
|
+
function isTechnicalUrl(url, baseUrl = "http://localhost") {
|
|
370
|
+
try {
|
|
371
|
+
return isTechnicalPath(new URL(url, baseUrl).pathname);
|
|
372
|
+
} catch {
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
369
376
|
function getRouteFromPathInfo(path) {
|
|
370
377
|
const normalizedPath = normalizePath(path);
|
|
371
378
|
for (const [prefix, routeName] of Object.entries(TECHNICAL_ROUTE_MAP)) {
|
|
@@ -379,6 +386,57 @@ function getRouteFromPathInfo(path) {
|
|
|
379
386
|
}
|
|
380
387
|
return null;
|
|
381
388
|
}
|
|
389
|
+
function getCanonicalPathForTechnicalPath(path, seoUrl) {
|
|
390
|
+
if (!isTechnicalPath(path) || !seoUrl || !("seoPathInfo" in seoUrl) || typeof seoUrl.seoPathInfo !== "string") {
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
const seoPathInfo = seoUrl.seoPathInfo.replace(/^\/+/, "");
|
|
394
|
+
const canonicalPath = seoPathInfo ? normalizePath(`/${seoPathInfo}`) : "/";
|
|
395
|
+
if (isTechnicalPath(canonicalPath)) return null;
|
|
396
|
+
return canonicalPath;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const CATEGORY_AGGREGATION_NAME = "categories";
|
|
400
|
+
const CATEGORY_COUNTS_AGGREGATION_NAME = "categories-counts";
|
|
401
|
+
const CATEGORY_PARENTS_AGGREGATION_NAME = "categories-parents";
|
|
402
|
+
const CATEGORY_FIELD = "categoriesRo.id";
|
|
403
|
+
function getCategoryFilterAggregations() {
|
|
404
|
+
return [
|
|
405
|
+
{
|
|
406
|
+
name: CATEGORY_AGGREGATION_NAME,
|
|
407
|
+
type: "entity",
|
|
408
|
+
definition: "category",
|
|
409
|
+
field: CATEGORY_FIELD
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
name: CATEGORY_COUNTS_AGGREGATION_NAME,
|
|
413
|
+
type: "terms",
|
|
414
|
+
field: CATEGORY_FIELD
|
|
415
|
+
}
|
|
416
|
+
];
|
|
417
|
+
}
|
|
418
|
+
function excludeRootCategory(entities, rootCategoryId) {
|
|
419
|
+
if (!entities) return [];
|
|
420
|
+
return entities.filter((entity) => entity.id !== rootCategoryId);
|
|
421
|
+
}
|
|
422
|
+
function getCategoryFilterPostFilter(categoryIds) {
|
|
423
|
+
return {
|
|
424
|
+
field: CATEGORY_FIELD,
|
|
425
|
+
type: "equalsAny",
|
|
426
|
+
value: categoryIds.join("|")
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
function resolveCategoryBucketCount(bucket) {
|
|
430
|
+
const parents = bucket[CATEGORY_PARENTS_AGGREGATION_NAME];
|
|
431
|
+
if (!parents?.buckets) {
|
|
432
|
+
return bucket.count;
|
|
433
|
+
}
|
|
434
|
+
let count = 0;
|
|
435
|
+
for (const parent of parents.buckets) {
|
|
436
|
+
count += parent.key ? 1 : parent.count;
|
|
437
|
+
}
|
|
438
|
+
return count;
|
|
439
|
+
}
|
|
382
440
|
|
|
383
441
|
const getFilter = (code, aggregation) => {
|
|
384
442
|
return {
|
|
@@ -390,6 +448,30 @@ const getFilter = (code, aggregation) => {
|
|
|
390
448
|
function isEntitiesAggregation(aggregation) {
|
|
391
449
|
return aggregation.entities !== void 0;
|
|
392
450
|
}
|
|
451
|
+
function getCategoryFilter(aggregation, countsAggregation) {
|
|
452
|
+
const countByCategoryId = /* @__PURE__ */ new Map();
|
|
453
|
+
for (const bucket of countsAggregation?.buckets ?? []) {
|
|
454
|
+
countByCategoryId.set(bucket.key, resolveCategoryBucketCount(bucket));
|
|
455
|
+
}
|
|
456
|
+
const entities = aggregation.entities.map((entity) => ({
|
|
457
|
+
...entity,
|
|
458
|
+
// Count per category. Used to sort here and exposed to consumers, but on
|
|
459
|
+
// the ES search route it counts every variant, so the filter UIs shipped
|
|
460
|
+
// in this repo do not render it.
|
|
461
|
+
count: countByCategoryId.get(entity.id)
|
|
462
|
+
})).sort(
|
|
463
|
+
(a, b) => (b.count ?? 0) - (a.count ?? 0) || getTranslatedProperty(a, "name").localeCompare(
|
|
464
|
+
getTranslatedProperty(b, "name")
|
|
465
|
+
)
|
|
466
|
+
);
|
|
467
|
+
return {
|
|
468
|
+
label: CATEGORY_AGGREGATION_NAME,
|
|
469
|
+
code: CATEGORY_AGGREGATION_NAME,
|
|
470
|
+
id: CATEGORY_AGGREGATION_NAME,
|
|
471
|
+
name: CATEGORY_AGGREGATION_NAME,
|
|
472
|
+
entities
|
|
473
|
+
};
|
|
474
|
+
}
|
|
393
475
|
function getListingFilters(aggregations) {
|
|
394
476
|
if (!aggregations) {
|
|
395
477
|
return [];
|
|
@@ -400,7 +482,16 @@ function getListingFilters(aggregations) {
|
|
|
400
482
|
for (const filterEntity of aggregation.entities) {
|
|
401
483
|
transformedFilters.push(getFilter(aggregationName, filterEntity));
|
|
402
484
|
}
|
|
403
|
-
} else if (
|
|
485
|
+
} else if (aggregationName === CATEGORY_AGGREGATION_NAME && isEntitiesAggregation(aggregation)) {
|
|
486
|
+
transformedFilters.push(
|
|
487
|
+
getCategoryFilter(
|
|
488
|
+
aggregation,
|
|
489
|
+
aggregations[CATEGORY_COUNTS_AGGREGATION_NAME]
|
|
490
|
+
)
|
|
491
|
+
);
|
|
492
|
+
} else if (!["properties", "options", CATEGORY_COUNTS_AGGREGATION_NAME].includes(
|
|
493
|
+
aggregationName
|
|
494
|
+
)) {
|
|
404
495
|
transformedFilters.push(getFilter(aggregationName, aggregation));
|
|
405
496
|
}
|
|
406
497
|
}
|
|
@@ -564,4 +655,4 @@ function canUseQuoteActions(quote) {
|
|
|
564
655
|
return ["replied"].includes(quote.stateMachineState?.technicalName ?? "");
|
|
565
656
|
}
|
|
566
657
|
|
|
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 };
|
|
658
|
+
export { CATEGORY_AGGREGATION_NAME, CATEGORY_COUNTS_AGGREGATION_NAME, CATEGORY_PARENTS_AGGREGATION_NAME, buildCdnImageUrl, buildUrlPrefix, canUseQuoteActions, downloadFile, encodeUrlPath, excludeRootCategory, generateCdnSrcSet, getBackgroundImageUrl, getBiggestThumbnailUrl, getCanonicalPathForTechnicalPath, getCategoryBreadcrumbs, getCategoryFilterAggregations, getCategoryFilterPostFilter, 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, isTechnicalUrl, normalizePath, relativeUrlSlash, urlIsAbsolute };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopware/helpers",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Shopware helpers for accessing API data",
|
|
5
5
|
"author": "Shopware",
|
|
6
6
|
"type": "module",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "git+https://github.com/shopware/frontends.git"
|
|
10
10
|
},
|
|
11
|
-
"homepage": "https://
|
|
11
|
+
"homepage": "https://developer.shopware.com/frontends/packages/helpers.html",
|
|
12
12
|
"keywords": [
|
|
13
13
|
"shopware",
|
|
14
14
|
"api",
|
|
@@ -32,20 +32,19 @@
|
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@biomejs/biome": "1.8.3",
|
|
36
35
|
"@typescript/native-preview": "7.0.0-dev.20260205.1",
|
|
37
|
-
"@vitest/coverage-v8": "4.1.
|
|
38
|
-
"happy-dom": "20.
|
|
39
|
-
"unbuild": "
|
|
40
|
-
"vitest": "4.1.
|
|
36
|
+
"@vitest/coverage-v8": "4.1.10",
|
|
37
|
+
"happy-dom": "20.11.1",
|
|
38
|
+
"unbuild": "3.6.1",
|
|
39
|
+
"vitest": "4.1.10",
|
|
41
40
|
"tsconfig": "0.0.0"
|
|
42
41
|
},
|
|
43
42
|
"scripts": {
|
|
44
43
|
"build": "unbuild && pnpm build:types",
|
|
45
44
|
"build:types": "tsgo ./src/*.ts --ignoreConfig --declaration --allowJs --emitDeclarationOnly --outDir ./temp --skipLibCheck",
|
|
46
45
|
"dev": "unbuild --stub",
|
|
47
|
-
"lint": "
|
|
48
|
-
"lint:fix": "
|
|
46
|
+
"lint": "oxlint . && oxfmt --check .",
|
|
47
|
+
"lint:fix": "oxlint --fix . && oxfmt --write . && pnpm run typecheck",
|
|
49
48
|
"typecheck": "tsgo --noEmit",
|
|
50
49
|
"test": "vitest run",
|
|
51
50
|
"test:watch": "vitest"
|