@commercetools-demo/puck-components 0.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/dist/index.d.mts +418 -0
- package/dist/index.d.ts +418 -0
- package/dist/index.js +2564 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2476 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +44 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ComponentConfig, Config } from '@measured/puck';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Locale catalogs owned by this package, keyed by language. `en.json` is the
|
|
6
|
+
* source of truth; other locales are generated by `scripts/translate-messages.mjs`
|
|
7
|
+
* and may be empty until that script is run (missing keys fall back to the
|
|
8
|
+
* English `defaultMessage`).
|
|
9
|
+
*/
|
|
10
|
+
declare const catalogs: Record<string, Record<string, string>>;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Sanitize rich-text HTML before it is injected via dangerouslySetInnerHTML.
|
|
14
|
+
*
|
|
15
|
+
* RichText/TextBlock/Card store author-controlled HTML produced by the TipTap
|
|
16
|
+
* editor, but content can also arrive from imports or older data, so we always
|
|
17
|
+
* sanitize at render time. isomorphic-dompurify works in both the browser and
|
|
18
|
+
* during SSR, so this is safe wherever the renderer runs.
|
|
19
|
+
*
|
|
20
|
+
* `style` and `class` are allowed because the editor emits inline styles
|
|
21
|
+
* (e.g. font-size from the typography control) and structural classes.
|
|
22
|
+
*/
|
|
23
|
+
declare const sanitizeHtml: (html: string | undefined | null) => string;
|
|
24
|
+
|
|
25
|
+
interface RichTextContentProps {
|
|
26
|
+
/** Rich-text HTML string; sanitized here before injection. */
|
|
27
|
+
html: string | undefined | null;
|
|
28
|
+
className?: string;
|
|
29
|
+
style?: React.CSSProperties;
|
|
30
|
+
}
|
|
31
|
+
declare const RichTextContent: React.FC<RichTextContentProps>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Datasource value types — Nimbus-free so both the render layer (this package)
|
|
35
|
+
* and the editor's `DatasourceField` widget can share them without the widget's
|
|
36
|
+
* Nimbus deps leaking into the render graph.
|
|
37
|
+
*/
|
|
38
|
+
type DatasourceType = 'product-by-sku' | 'products-by-sku';
|
|
39
|
+
interface DatasourceValue {
|
|
40
|
+
type: DatasourceType;
|
|
41
|
+
skus: string[];
|
|
42
|
+
/** Pre-resolved by the server on published/preview endpoints — use directly in render. */
|
|
43
|
+
resolvedData?: unknown;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
declare const getLocalizedText: (obj?: Record<string, string> | null) => string;
|
|
47
|
+
declare const formatPrice: (centAmount: number, currencyCode?: string, fractionDigits?: number) => string;
|
|
48
|
+
declare const getFirstPrice: (product: unknown) => {
|
|
49
|
+
centAmount: number;
|
|
50
|
+
currencyCode: string;
|
|
51
|
+
fractionDigits: number;
|
|
52
|
+
} | null;
|
|
53
|
+
declare const getProductImage: (product: unknown) => string | null;
|
|
54
|
+
declare const getProductName: (product: unknown) => string;
|
|
55
|
+
declare const getProductSlug: (product: unknown) => string;
|
|
56
|
+
declare const getProductSku: (product: unknown) => string;
|
|
57
|
+
/** How a product link is built — from the product slug or its SKU. */
|
|
58
|
+
type ProductLinkWith = 'slug' | 'sku';
|
|
59
|
+
/**
|
|
60
|
+
* Build a storefront link to a product.
|
|
61
|
+
*
|
|
62
|
+
* Joins a sanitised base URL with the product's slug or SKU. The base is
|
|
63
|
+
* trimmed, stripped of leading/trailing slashes and collapsed of any double
|
|
64
|
+
* slashes, then re-prefixed with a single "/", e.g.
|
|
65
|
+
* resolveProductLink(p, 'sku', '/product/') → '/product/123' (sku 123)
|
|
66
|
+
* resolveProductLink(p, 'slug', '/slug/p/') → '/slug/p/test' (slug "test")
|
|
67
|
+
*/
|
|
68
|
+
declare const resolveProductLink: (product: unknown, linkWith?: ProductLinkWith, baseUrl?: string) => string;
|
|
69
|
+
/** Props every product-linking component shares. */
|
|
70
|
+
interface ProductLinkProps {
|
|
71
|
+
linkWith: ProductLinkWith;
|
|
72
|
+
baseUrl: string;
|
|
73
|
+
}
|
|
74
|
+
/** Default values for the shared link props — spread into a component's defaultProps. */
|
|
75
|
+
declare const productLinkDefaults: ProductLinkProps;
|
|
76
|
+
declare const colors: {
|
|
77
|
+
primary: string;
|
|
78
|
+
primaryHover: string;
|
|
79
|
+
lightBg: string;
|
|
80
|
+
lightBorder: string;
|
|
81
|
+
text: string;
|
|
82
|
+
textMuted: string;
|
|
83
|
+
border: string;
|
|
84
|
+
bg: string;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Page content-width scale. `X` is the base column unit; each step doubles up
|
|
89
|
+
* to a wide layout, and `full` removes the constraint (edge-to-edge).
|
|
90
|
+
*/
|
|
91
|
+
type ContentWidth = 'x' | '2x' | '3x' | '4x' | '5x' | '6x' | 'full';
|
|
92
|
+
declare const CONTENT_WIDTHS: Record<ContentWidth, string>;
|
|
93
|
+
interface RootProps {
|
|
94
|
+
title?: string;
|
|
95
|
+
backgroundColor?: string;
|
|
96
|
+
contentWidth?: ContentWidth;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Root render — the page wrapper. Applies the background and centres the content
|
|
100
|
+
* column at the configured width. Nimbus-free (plain elements + inline styles).
|
|
101
|
+
*/
|
|
102
|
+
declare const renderRoot: ({ children, backgroundColor, contentWidth, }: RootProps & {
|
|
103
|
+
children?: React.ReactNode;
|
|
104
|
+
}) => React.ReactElement;
|
|
105
|
+
|
|
106
|
+
interface HeroProps {
|
|
107
|
+
heading: string;
|
|
108
|
+
headingFontSize?: string;
|
|
109
|
+
subheading?: string;
|
|
110
|
+
subheadingFontSize?: string;
|
|
111
|
+
backgroundImage?: string;
|
|
112
|
+
ctaText?: string;
|
|
113
|
+
ctaUrl?: string;
|
|
114
|
+
layout?: 'centered' | 'left-aligned';
|
|
115
|
+
minHeight?: string;
|
|
116
|
+
}
|
|
117
|
+
declare const renderHero: NonNullable<ComponentConfig<HeroProps>['render']>;
|
|
118
|
+
|
|
119
|
+
interface RichTextProps {
|
|
120
|
+
content: string;
|
|
121
|
+
align?: 'left' | 'center' | 'right';
|
|
122
|
+
maxWidth?: string;
|
|
123
|
+
padding?: string;
|
|
124
|
+
}
|
|
125
|
+
declare const renderRichText: NonNullable<ComponentConfig<RichTextProps>['render']>;
|
|
126
|
+
|
|
127
|
+
interface GridProps {
|
|
128
|
+
columnCount?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
129
|
+
rowCount?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
130
|
+
gap?: string;
|
|
131
|
+
padding?: string;
|
|
132
|
+
}
|
|
133
|
+
declare const renderGrid: NonNullable<ComponentConfig<GridProps>['render']>;
|
|
134
|
+
|
|
135
|
+
interface ColumnsProps {
|
|
136
|
+
columnCount?: 2 | 3 | 4;
|
|
137
|
+
gap?: string;
|
|
138
|
+
padding?: string;
|
|
139
|
+
}
|
|
140
|
+
declare const renderColumns: NonNullable<ComponentConfig<ColumnsProps>['render']>;
|
|
141
|
+
|
|
142
|
+
interface ImageProps {
|
|
143
|
+
src: string;
|
|
144
|
+
alt?: string;
|
|
145
|
+
caption?: string;
|
|
146
|
+
width?: string;
|
|
147
|
+
height?: string;
|
|
148
|
+
objectFit?: 'cover' | 'contain' | 'fill';
|
|
149
|
+
borderRadius?: string;
|
|
150
|
+
align?: 'left' | 'center' | 'right';
|
|
151
|
+
}
|
|
152
|
+
declare const renderImage: NonNullable<ComponentConfig<ImageProps>['render']>;
|
|
153
|
+
|
|
154
|
+
interface ButtonProps {
|
|
155
|
+
label: string;
|
|
156
|
+
href?: string;
|
|
157
|
+
variant?: 'primary' | 'secondary' | 'outline';
|
|
158
|
+
size?: 'sm' | 'md' | 'lg';
|
|
159
|
+
fontSize?: string;
|
|
160
|
+
align?: 'left' | 'center' | 'right';
|
|
161
|
+
openInNewTab?: boolean;
|
|
162
|
+
}
|
|
163
|
+
declare const renderButton: NonNullable<ComponentConfig<ButtonProps>['render']>;
|
|
164
|
+
|
|
165
|
+
interface CardProps {
|
|
166
|
+
title: string;
|
|
167
|
+
titleFontSize?: string;
|
|
168
|
+
body?: string;
|
|
169
|
+
imageUrl?: string;
|
|
170
|
+
ctaText?: string;
|
|
171
|
+
ctaUrl?: string;
|
|
172
|
+
shadow?: boolean;
|
|
173
|
+
borderRadius?: string;
|
|
174
|
+
}
|
|
175
|
+
declare const renderCard: NonNullable<ComponentConfig<CardProps>['render']>;
|
|
176
|
+
|
|
177
|
+
interface SpacerProps {
|
|
178
|
+
height?: string;
|
|
179
|
+
showLine?: boolean;
|
|
180
|
+
lineColor?: string;
|
|
181
|
+
}
|
|
182
|
+
declare const renderSpacer: NonNullable<ComponentConfig<SpacerProps>['render']>;
|
|
183
|
+
|
|
184
|
+
interface ProductTeaserProps extends ProductLinkProps {
|
|
185
|
+
datasource: DatasourceValue;
|
|
186
|
+
richText: string;
|
|
187
|
+
}
|
|
188
|
+
declare const renderProductTeaser: NonNullable<ComponentConfig<ProductTeaserProps>['render']>;
|
|
189
|
+
|
|
190
|
+
interface HeroBannerProps {
|
|
191
|
+
title: string;
|
|
192
|
+
subtitle: string;
|
|
193
|
+
image: string;
|
|
194
|
+
}
|
|
195
|
+
declare const renderHeroBanner: NonNullable<ComponentConfig<HeroBannerProps>['render']>;
|
|
196
|
+
|
|
197
|
+
interface TextBlockProps {
|
|
198
|
+
content: string;
|
|
199
|
+
}
|
|
200
|
+
declare const renderTextBlock: NonNullable<ComponentConfig<TextBlockProps>['render']>;
|
|
201
|
+
|
|
202
|
+
interface CategoryGridProps {
|
|
203
|
+
category1Image: string;
|
|
204
|
+
category1Label: string;
|
|
205
|
+
category1Link: string;
|
|
206
|
+
category2Image: string;
|
|
207
|
+
category2Label: string;
|
|
208
|
+
category2Link: string;
|
|
209
|
+
category3Image: string;
|
|
210
|
+
category3Label: string;
|
|
211
|
+
category3Link: string;
|
|
212
|
+
category4Image: string;
|
|
213
|
+
category4Label: string;
|
|
214
|
+
category4Link: string;
|
|
215
|
+
}
|
|
216
|
+
declare const renderCategoryGrid: NonNullable<ComponentConfig<CategoryGridProps>['render']>;
|
|
217
|
+
|
|
218
|
+
interface CategoryHeroProps {
|
|
219
|
+
title: string;
|
|
220
|
+
subtitle: string;
|
|
221
|
+
image: string;
|
|
222
|
+
ctaText: string;
|
|
223
|
+
ctaLink: string;
|
|
224
|
+
}
|
|
225
|
+
declare const renderCategoryHero: NonNullable<ComponentConfig<CategoryHeroProps>['render']>;
|
|
226
|
+
|
|
227
|
+
interface CheckoutPromoBannerProps {
|
|
228
|
+
title: string;
|
|
229
|
+
message: string;
|
|
230
|
+
ctaText: string;
|
|
231
|
+
ctaLink: string;
|
|
232
|
+
}
|
|
233
|
+
declare const renderCheckoutPromoBanner: NonNullable<ComponentConfig<CheckoutPromoBannerProps>['render']>;
|
|
234
|
+
|
|
235
|
+
interface CountdownBannerProps {
|
|
236
|
+
headline: string;
|
|
237
|
+
subline: string;
|
|
238
|
+
endDate: string;
|
|
239
|
+
ctaText: string;
|
|
240
|
+
ctaLink: string;
|
|
241
|
+
background: string;
|
|
242
|
+
}
|
|
243
|
+
declare const renderCountdownBanner: NonNullable<ComponentConfig<CountdownBannerProps>['render']>;
|
|
244
|
+
|
|
245
|
+
interface CrossSellBlockProps extends ProductLinkProps {
|
|
246
|
+
title: string;
|
|
247
|
+
products: DatasourceValue;
|
|
248
|
+
ctaText: string;
|
|
249
|
+
}
|
|
250
|
+
declare const renderCrossSellBlock: NonNullable<ComponentConfig<CrossSellBlockProps>['render']>;
|
|
251
|
+
|
|
252
|
+
interface DeliveryMessageProps {
|
|
253
|
+
message: string;
|
|
254
|
+
threshold: string;
|
|
255
|
+
}
|
|
256
|
+
declare const renderDeliveryMessage: NonNullable<ComponentConfig<DeliveryMessageProps>['render']>;
|
|
257
|
+
|
|
258
|
+
interface DividerProps {
|
|
259
|
+
lineStyle: 'solid' | 'dashed' | 'dotted';
|
|
260
|
+
spacing: string;
|
|
261
|
+
}
|
|
262
|
+
declare const renderDivider: NonNullable<ComponentConfig<DividerProps>['render']>;
|
|
263
|
+
|
|
264
|
+
interface EmptyStateProps {
|
|
265
|
+
image: string;
|
|
266
|
+
title: string;
|
|
267
|
+
description: string;
|
|
268
|
+
ctaText: string;
|
|
269
|
+
ctaLink: string;
|
|
270
|
+
}
|
|
271
|
+
declare const renderEmptyState: NonNullable<ComponentConfig<EmptyStateProps>['render']>;
|
|
272
|
+
|
|
273
|
+
interface FAQAccordionProps {
|
|
274
|
+
question1: string;
|
|
275
|
+
answer1: string;
|
|
276
|
+
question2: string;
|
|
277
|
+
answer2: string;
|
|
278
|
+
question3: string;
|
|
279
|
+
answer3: string;
|
|
280
|
+
question4: string;
|
|
281
|
+
answer4: string;
|
|
282
|
+
}
|
|
283
|
+
declare const renderFAQAccordion: NonNullable<ComponentConfig<FAQAccordionProps>['render']>;
|
|
284
|
+
|
|
285
|
+
interface FooterBlockProps {
|
|
286
|
+
column1: string;
|
|
287
|
+
column2: string;
|
|
288
|
+
column3: string;
|
|
289
|
+
copyright: string;
|
|
290
|
+
}
|
|
291
|
+
declare const renderFooterBlock: NonNullable<ComponentConfig<FooterBlockProps>['render']>;
|
|
292
|
+
|
|
293
|
+
interface ImageBlockProps {
|
|
294
|
+
image: string;
|
|
295
|
+
caption: string;
|
|
296
|
+
link: string;
|
|
297
|
+
}
|
|
298
|
+
declare const renderImageBlock: NonNullable<ComponentConfig<ImageBlockProps>['render']>;
|
|
299
|
+
|
|
300
|
+
interface NewsletterSignupProps {
|
|
301
|
+
title: string;
|
|
302
|
+
subtitle: string;
|
|
303
|
+
ctaText: string;
|
|
304
|
+
placeholder: string;
|
|
305
|
+
}
|
|
306
|
+
declare const renderNewsletterSignup: NonNullable<ComponentConfig<NewsletterSignupProps>['render']>;
|
|
307
|
+
|
|
308
|
+
interface ProductBannerProps extends ProductLinkProps {
|
|
309
|
+
title: string;
|
|
310
|
+
description: string;
|
|
311
|
+
ctaText: string;
|
|
312
|
+
ctaLink: string;
|
|
313
|
+
product: DatasourceValue;
|
|
314
|
+
productOnLeft: boolean;
|
|
315
|
+
background: string;
|
|
316
|
+
}
|
|
317
|
+
declare const renderProductBanner: NonNullable<ComponentConfig<ProductBannerProps>['render']>;
|
|
318
|
+
|
|
319
|
+
interface ProductGridHeaderProps {
|
|
320
|
+
title: string;
|
|
321
|
+
description: string;
|
|
322
|
+
}
|
|
323
|
+
declare const renderProductGridHeader: NonNullable<ComponentConfig<ProductGridHeaderProps>['render']>;
|
|
324
|
+
|
|
325
|
+
interface ProductSliderProps extends ProductLinkProps {
|
|
326
|
+
title: string;
|
|
327
|
+
subtitle: string;
|
|
328
|
+
products: DatasourceValue;
|
|
329
|
+
}
|
|
330
|
+
declare const renderProductSlider: NonNullable<ComponentConfig<ProductSliderProps>['render']>;
|
|
331
|
+
|
|
332
|
+
interface PromotionalBannerProps {
|
|
333
|
+
image: string;
|
|
334
|
+
title: string;
|
|
335
|
+
subtitle: string;
|
|
336
|
+
ctaText: string;
|
|
337
|
+
ctaLink: string;
|
|
338
|
+
background: string;
|
|
339
|
+
}
|
|
340
|
+
declare const renderPromotionalBanner: NonNullable<ComponentConfig<PromotionalBannerProps>['render']>;
|
|
341
|
+
|
|
342
|
+
interface RelatedProductsSliderProps extends ProductLinkProps {
|
|
343
|
+
title: string;
|
|
344
|
+
subtitle: string;
|
|
345
|
+
products: DatasourceValue;
|
|
346
|
+
}
|
|
347
|
+
declare const renderRelatedProductsSlider: NonNullable<ComponentConfig<RelatedProductsSliderProps>['render']>;
|
|
348
|
+
|
|
349
|
+
interface SocialLinksProps {
|
|
350
|
+
link1Label: string;
|
|
351
|
+
link1Url: string;
|
|
352
|
+
link2Label: string;
|
|
353
|
+
link2Url: string;
|
|
354
|
+
link3Label: string;
|
|
355
|
+
link3Url: string;
|
|
356
|
+
link4Label: string;
|
|
357
|
+
link4Url: string;
|
|
358
|
+
}
|
|
359
|
+
declare const renderSocialLinks: NonNullable<ComponentConfig<SocialLinksProps>['render']>;
|
|
360
|
+
|
|
361
|
+
interface TabContentProps {
|
|
362
|
+
tabLabel: string;
|
|
363
|
+
content: string;
|
|
364
|
+
}
|
|
365
|
+
declare const renderTabContent: NonNullable<ComponentConfig<TabContentProps>['render']>;
|
|
366
|
+
|
|
367
|
+
interface TestimonialsSliderProps {
|
|
368
|
+
quote1: string;
|
|
369
|
+
author1: string;
|
|
370
|
+
role1: string;
|
|
371
|
+
quote2: string;
|
|
372
|
+
author2: string;
|
|
373
|
+
role2: string;
|
|
374
|
+
quote3: string;
|
|
375
|
+
author3: string;
|
|
376
|
+
role3: string;
|
|
377
|
+
}
|
|
378
|
+
declare const renderTestimonialsSlider: NonNullable<ComponentConfig<TestimonialsSliderProps>['render']>;
|
|
379
|
+
|
|
380
|
+
interface ThankYouContentProps {
|
|
381
|
+
headline: string;
|
|
382
|
+
message: string;
|
|
383
|
+
ctaText: string;
|
|
384
|
+
ctaLink: string;
|
|
385
|
+
}
|
|
386
|
+
declare const renderThankYouContent: NonNullable<ComponentConfig<ThankYouContentProps>['render']>;
|
|
387
|
+
|
|
388
|
+
interface TrustBadgesProps {
|
|
389
|
+
badge1Icon: string;
|
|
390
|
+
badge1Label: string;
|
|
391
|
+
badge2Icon: string;
|
|
392
|
+
badge2Label: string;
|
|
393
|
+
badge3Icon: string;
|
|
394
|
+
badge3Label: string;
|
|
395
|
+
badge4Icon: string;
|
|
396
|
+
badge4Label: string;
|
|
397
|
+
}
|
|
398
|
+
declare const renderTrustBadges: NonNullable<ComponentConfig<TrustBadgesProps>['render']>;
|
|
399
|
+
|
|
400
|
+
interface VideoBlockProps {
|
|
401
|
+
videoUrl: string;
|
|
402
|
+
posterImage: string;
|
|
403
|
+
title: string;
|
|
404
|
+
caption: string;
|
|
405
|
+
}
|
|
406
|
+
declare const renderVideoBlock: NonNullable<ComponentConfig<VideoBlockProps>['render']>;
|
|
407
|
+
|
|
408
|
+
interface WebsiteLogoProps {
|
|
409
|
+
logo: string;
|
|
410
|
+
maxWidth: string;
|
|
411
|
+
maxHeight: string;
|
|
412
|
+
}
|
|
413
|
+
declare const renderWebsiteLogo: NonNullable<ComponentConfig<WebsiteLogoProps>['render']>;
|
|
414
|
+
|
|
415
|
+
declare function createRenderConfig(): Config;
|
|
416
|
+
declare const defaultRenderConfig: Config;
|
|
417
|
+
|
|
418
|
+
export { type ButtonProps, CONTENT_WIDTHS, type CardProps, type CategoryGridProps, type CategoryHeroProps, type CheckoutPromoBannerProps, type ColumnsProps, type ContentWidth, type CountdownBannerProps, type CrossSellBlockProps, type DatasourceType, type DatasourceValue, type DeliveryMessageProps, type DividerProps, type EmptyStateProps, type FAQAccordionProps, type FooterBlockProps, type GridProps, type HeroBannerProps, type HeroProps, type ImageBlockProps, type ImageProps, type NewsletterSignupProps, type ProductBannerProps, type ProductGridHeaderProps, type ProductLinkProps, type ProductLinkWith, type ProductSliderProps, type ProductTeaserProps, type PromotionalBannerProps, type RelatedProductsSliderProps, RichTextContent, type RichTextContentProps, type RichTextProps, type RootProps, type SocialLinksProps, type SpacerProps, type TabContentProps, type TestimonialsSliderProps, type TextBlockProps, type ThankYouContentProps, type TrustBadgesProps, type VideoBlockProps, type WebsiteLogoProps, colors, createRenderConfig, defaultRenderConfig, formatPrice, getFirstPrice, getLocalizedText, getProductImage, getProductName, getProductSku, getProductSlug, catalogs as intlCatalogs, productLinkDefaults, renderButton, renderCard, renderCategoryGrid, renderCategoryHero, renderCheckoutPromoBanner, renderColumns, renderCountdownBanner, renderCrossSellBlock, renderDeliveryMessage, renderDivider, renderEmptyState, renderFAQAccordion, renderFooterBlock, renderGrid, renderHero, renderHeroBanner, renderImage, renderImageBlock, renderNewsletterSignup, renderProductBanner, renderProductGridHeader, renderProductSlider, renderProductTeaser, renderPromotionalBanner, renderRelatedProductsSlider, renderRichText, renderRoot, renderSocialLinks, renderSpacer, renderTabContent, renderTestimonialsSlider, renderTextBlock, renderThankYouContent, renderTrustBadges, renderVideoBlock, renderWebsiteLogo, resolveProductLink, sanitizeHtml };
|