@adobe-commerce/elsie 1.3.0-alpha15 → 1.3.0-alpha16
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/package.json +1 -1
- package/src/lib/aem/assets.ts +207 -0
package/package.json
CHANGED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { provider as UI, Image } from '@adobe-commerce/elsie/components';
|
|
2
|
+
import { getConfigValue } from '@adobe-commerce/elsie/lib/aem/configs';
|
|
3
|
+
|
|
4
|
+
interface AemAssetsParams {
|
|
5
|
+
quality?: number;
|
|
6
|
+
format?: string;
|
|
7
|
+
crop?: {
|
|
8
|
+
xOrigin?: number;
|
|
9
|
+
yOrigin?: number;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
};
|
|
13
|
+
size?: {
|
|
14
|
+
width?: number;
|
|
15
|
+
height?: number;
|
|
16
|
+
};
|
|
17
|
+
width?: number;
|
|
18
|
+
height?: number;
|
|
19
|
+
[key: string]: any;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface AemAssetsImageConfig {
|
|
23
|
+
wrapper?: HTMLElement;
|
|
24
|
+
alias: string;
|
|
25
|
+
params: AemAssetsParams;
|
|
26
|
+
imageProps: {
|
|
27
|
+
src: string;
|
|
28
|
+
width?: number;
|
|
29
|
+
height?: number;
|
|
30
|
+
[key: string]: any;
|
|
31
|
+
};
|
|
32
|
+
src?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface RenderContext {
|
|
36
|
+
replaceWith: (element: HTMLElement) => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function isAemAssetsEnabled(): boolean {
|
|
40
|
+
const config = getConfigValue('commerce-assets-enabled');
|
|
41
|
+
|
|
42
|
+
return config && (
|
|
43
|
+
(typeof config === 'string' && config.toLowerCase() === 'true')
|
|
44
|
+
|| (typeof config === 'boolean' && config === true)
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function getDefaultAemAssetsOptimizationParams(): { quality: number; format: string } {
|
|
49
|
+
// See: https://adobe-aem-assets-delivery-experimental.redoc.ly/
|
|
50
|
+
return {
|
|
51
|
+
quality: 80,
|
|
52
|
+
format: 'webp',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Normalizes the given URL to ensure it is a valid URL.
|
|
58
|
+
* @param {string} url - The URL to normalize.
|
|
59
|
+
* @returns {string} The normalized URL.
|
|
60
|
+
*/
|
|
61
|
+
function normalizeUrl(url: string): string {
|
|
62
|
+
let imageUrl = url;
|
|
63
|
+
|
|
64
|
+
if (imageUrl.startsWith('//')) {
|
|
65
|
+
// Use current window's protocol.
|
|
66
|
+
const { protocol } = window.location;
|
|
67
|
+
console.log('protocol', protocol);
|
|
68
|
+
imageUrl = protocol + imageUrl;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return imageUrl;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function isAemAssetsUrl(url: string | URL): boolean {
|
|
75
|
+
const assetsUrl = typeof url === 'string' ? new URL(normalizeUrl(url)) : url;
|
|
76
|
+
|
|
77
|
+
if (!assetsUrl.pathname.startsWith('/adobe/assets/urn:aaid:aem')) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function generateAemAssetsOptimizedUrl(url: string, alias: string, params: AemAssetsParams = {}): string {
|
|
85
|
+
const defaultParams = getDefaultAemAssetsOptimizationParams();
|
|
86
|
+
const mergedParams: AemAssetsParams = { ...defaultParams, ...params };
|
|
87
|
+
|
|
88
|
+
// Destructure the ones that need special handling
|
|
89
|
+
const {
|
|
90
|
+
format,
|
|
91
|
+
crop,
|
|
92
|
+
size,
|
|
93
|
+
...optimizedParams
|
|
94
|
+
} = mergedParams;
|
|
95
|
+
|
|
96
|
+
const searchParams = new URLSearchParams(optimizedParams);
|
|
97
|
+
|
|
98
|
+
if (crop) {
|
|
99
|
+
const [xOrigin, yOrigin] = [crop.xOrigin || 0, crop.yOrigin || 0];
|
|
100
|
+
const [width, height] = [crop.width || 100, crop.height || 100];
|
|
101
|
+
|
|
102
|
+
const cropTransform = `${xOrigin}p,${yOrigin}p,${width}p,${height}p`;
|
|
103
|
+
searchParams.set('crop', cropTransform);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Both values must be present
|
|
107
|
+
if (size && size.width && size.height) {
|
|
108
|
+
searchParams.set('size', `${size.width},${size.height}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return `${url}/as/${alias}.${format}?${searchParams.toString()}`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function tryGenerateAemAssetsOptimizedUrl(url: string, alias: string, params: AemAssetsParams = {}): string {
|
|
115
|
+
const assetsEnabled = isAemAssetsEnabled();
|
|
116
|
+
|
|
117
|
+
if (!(assetsEnabled)) {
|
|
118
|
+
// No-op, doesn't do anything.
|
|
119
|
+
return url;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const assetsUrl = new URL(normalizeUrl(url));
|
|
123
|
+
|
|
124
|
+
if (!isAemAssetsUrl(assetsUrl)) {
|
|
125
|
+
// Not an AEM Assets URL, so no-op.
|
|
126
|
+
return url;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const base = assetsUrl.origin + assetsUrl.pathname;
|
|
130
|
+
return generateAemAssetsOptimizedUrl(base, alias, params);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function makeAemAssetsImageSlot(
|
|
134
|
+
config: AemAssetsImageConfig,
|
|
135
|
+
) {
|
|
136
|
+
return (ctx: RenderContext) => {
|
|
137
|
+
const {
|
|
138
|
+
wrapper,
|
|
139
|
+
alias,
|
|
140
|
+
params,
|
|
141
|
+
imageProps,
|
|
142
|
+
src,
|
|
143
|
+
} = config;
|
|
144
|
+
|
|
145
|
+
const container = wrapper ?? document.createElement('div');
|
|
146
|
+
const imageSrc = generateAemAssetsOptimizedUrl(src || imageProps.src, alias, params);
|
|
147
|
+
|
|
148
|
+
UI.render(Image as any, {
|
|
149
|
+
...imageProps,
|
|
150
|
+
|
|
151
|
+
src: imageSrc,
|
|
152
|
+
params: {
|
|
153
|
+
width: params.width,
|
|
154
|
+
|
|
155
|
+
// If not null, they will be applied by default.
|
|
156
|
+
// And they are not compatible with the AEM Assets API.
|
|
157
|
+
crop: null,
|
|
158
|
+
fit: null,
|
|
159
|
+
auto: null,
|
|
160
|
+
},
|
|
161
|
+
})(container);
|
|
162
|
+
|
|
163
|
+
ctx.replaceWith(container);
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function tryRenderAemAssetsImage(ctx: RenderContext, config: AemAssetsImageConfig): void {
|
|
168
|
+
// Renders an equivalent of the default image.
|
|
169
|
+
function renderDefaultImage(): void {
|
|
170
|
+
const container = config.wrapper ?? document.createElement('div');
|
|
171
|
+
const { imageProps } = config;
|
|
172
|
+
|
|
173
|
+
(UI.render as any)(Image, imageProps)(container);
|
|
174
|
+
ctx.replaceWith(container);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const assetsEnabled = isAemAssetsEnabled();
|
|
178
|
+
|
|
179
|
+
if (!(assetsEnabled)) {
|
|
180
|
+
// No-op, render the default image.
|
|
181
|
+
renderDefaultImage();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const { imageProps, src, ...slotConfig } = config;
|
|
186
|
+
const assetsUrl = new URL(normalizeUrl(src ?? imageProps.src));
|
|
187
|
+
|
|
188
|
+
if (!isAemAssetsUrl(assetsUrl)) {
|
|
189
|
+
// Not an AEM Assets URL, so render the default image.
|
|
190
|
+
renderDefaultImage();
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
makeAemAssetsImageSlot({
|
|
195
|
+
// Use the default image props for params and src.
|
|
196
|
+
// Unless overriden by the slot config.
|
|
197
|
+
src: assetsUrl.toString(),
|
|
198
|
+
params: {
|
|
199
|
+
width: imageProps.width,
|
|
200
|
+
height: imageProps.height,
|
|
201
|
+
...slotConfig.params,
|
|
202
|
+
},
|
|
203
|
+
imageProps,
|
|
204
|
+
alias: slotConfig.alias,
|
|
205
|
+
wrapper: slotConfig.wrapper,
|
|
206
|
+
})(ctx);
|
|
207
|
+
}
|