@decocms/apps 1.8.0 → 1.9.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/commerce/components/Image.tsx +18 -0
- package/commerce/sdk/url.ts +46 -3
- package/package.json +1 -1
|
@@ -88,6 +88,20 @@ function optimizeShopify(originalSrc: string, width: number, height?: number): s
|
|
|
88
88
|
export function getOptimizedMediaUrl(opts: OptimizationOptions): string {
|
|
89
89
|
const { originalSrc, width, height, fit } = opts;
|
|
90
90
|
|
|
91
|
+
// Defensive: an upstream CMS payload occasionally has missing/null image
|
|
92
|
+
// fields. Crashing the entire React tree on `undefined.startsWith` would
|
|
93
|
+
// take down the whole page. Return an empty string so the resulting
|
|
94
|
+
// `<img>` is rendered with no src — the browser shows the broken-image
|
|
95
|
+
// placeholder and SSR completes cleanly.
|
|
96
|
+
if (typeof originalSrc !== "string" || originalSrc.length === 0) {
|
|
97
|
+
if (typeof process !== "undefined" && process.env?.NODE_ENV !== "production") {
|
|
98
|
+
console.warn(
|
|
99
|
+
`[Image] getOptimizedMediaUrl called with empty/undefined src — rendering empty src instead of crashing.`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
return "";
|
|
103
|
+
}
|
|
104
|
+
|
|
91
105
|
if (originalSrc.startsWith("data:")) {
|
|
92
106
|
return originalSrc;
|
|
93
107
|
}
|
|
@@ -126,6 +140,10 @@ export function getSrcSet(
|
|
|
126
140
|
fit?: FitOptions,
|
|
127
141
|
factors: number[] = FACTORS,
|
|
128
142
|
): string | undefined {
|
|
143
|
+
if (typeof originalSrc !== "string" || originalSrc.length === 0) {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
|
|
129
147
|
const entries: string[] = [];
|
|
130
148
|
|
|
131
149
|
for (const factor of factors) {
|
package/commerce/sdk/url.ts
CHANGED
|
@@ -1,9 +1,52 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Options for {@link relative}.
|
|
3
|
+
*
|
|
4
|
+
* `stripSearchParams` is the primitive escape hatch used by sites that
|
|
5
|
+
* need to drop platform-specific query keys (commonly VTEX's `idsku`
|
|
6
|
+
* / `skuId`) before linking to a PDP. Any keys not listed are kept.
|
|
7
|
+
*
|
|
8
|
+
* Sites previously hand-rolled this by forking `relative()` locally
|
|
9
|
+
* with a `removeIdSku?: boolean` flag — see the migration guide and
|
|
10
|
+
* the `local-framework-duplicate` audit rule in `@decocms/start`.
|
|
11
|
+
*/
|
|
12
|
+
export interface RelativeOptions {
|
|
13
|
+
stripSearchParams?: string[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Convert an absolute or relative URL string into a path + search
|
|
18
|
+
* fragment safe to feed into `<Link to={…} />` or `<a href={…}>`.
|
|
19
|
+
*
|
|
20
|
+
* - Returns `undefined` when `link` is falsy (the empty / undefined
|
|
21
|
+
* case is the common "no permalink yet" branch in product cards).
|
|
22
|
+
* - Returns the original string when URL parsing fails — preserves
|
|
23
|
+
* pre-existing behaviour for malformed inputs.
|
|
24
|
+
* - When `options.stripSearchParams` is non-empty, every listed key
|
|
25
|
+
* is removed from the resulting `?...` portion. Keys not present
|
|
26
|
+
* in the input are silently ignored. The pathname is never
|
|
27
|
+
* touched — only search params.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* relative("/p/foo"); // "/p/foo"
|
|
31
|
+
* relative("https://x.com/p/foo?a=1"); // "/p/foo?a=1"
|
|
32
|
+
* relative("/p/foo?idsku=1&keep=2", {
|
|
33
|
+
* stripSearchParams: ["idsku"],
|
|
34
|
+
* }); // "/p/foo?keep=2"
|
|
35
|
+
* relative(undefined); // undefined
|
|
36
|
+
*/
|
|
37
|
+
export function relative(link?: string, options?: RelativeOptions): string | undefined {
|
|
2
38
|
if (!link) return undefined;
|
|
3
39
|
try {
|
|
4
40
|
const linkUrl = new URL(link, "https://localhost");
|
|
5
|
-
|
|
41
|
+
const stripKeys = options?.stripSearchParams;
|
|
42
|
+
if (stripKeys && stripKeys.length > 0) {
|
|
43
|
+
for (const key of stripKeys) {
|
|
44
|
+
linkUrl.searchParams.delete(key);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const search = linkUrl.searchParams.toString();
|
|
48
|
+
return `${linkUrl.pathname}${search ? `?${search}` : ""}`;
|
|
6
49
|
} catch {
|
|
7
50
|
return link;
|
|
8
51
|
}
|
|
9
|
-
}
|
|
52
|
+
}
|