@adobe-commerce/elsie 2.1.0-beta.5 → 2.1.0-beta.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @adobe-commerce/elsie
2
2
 
3
+ ## 2.1.0-beta.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 406a36d: Restore the existing Slot implementation and temporarily downgrade
8
+ Preact from 10.29 to 10.28.4, the latest pre-10.29 release, to avoid
9
+ intermittent `insertBefore` crashes in AEM Asset image Slots while the
10
+ underlying DOM graft and render-root ownership changes are developed and
11
+ validated separately.
12
+
3
13
  ## 2.1.0-beta.5
4
14
 
5
15
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe-commerce/elsie",
3
- "version": "2.1.0-beta.5",
3
+ "version": "2.1.0-beta.6",
4
4
  "license": "SEE LICENSE IN LICENSE.md",
5
5
  "description": "Domain Package SDK",
6
6
  "engines": {
@@ -87,7 +87,7 @@
87
87
  "identity-obj-proxy": "^3.0.0",
88
88
  "jest": "^30.4.2",
89
89
  "jest-environment-jsdom": "^30.0.0",
90
- "preact": "~10.29.7",
90
+ "preact": "~10.28.4",
91
91
  "preact-i18n": "2.4.0-preactx",
92
92
  "preact-render-to-string": "^5.1.4",
93
93
  "prettier": "^3.8.4",
@@ -238,7 +238,7 @@ export function tryGenerateAemAssetsOptimizedUrl(
238
238
 
239
239
  /** Creates a slot that renders an AEM Assets image. */
240
240
  export function makeAemAssetsImageSlot(config: AemAssetsImageSlotConfig) {
241
- return async (ctx: RenderContext): Promise<void> => {
241
+ return (ctx: RenderContext) => {
242
242
  const { wrapper, alias, params, imageProps } = config;
243
243
 
244
244
  if (!imageProps.src) {
@@ -274,25 +274,17 @@ export function makeAemAssetsImageSlot(config: AemAssetsImageSlotConfig) {
274
274
  params: imageComponentParams,
275
275
  };
276
276
 
277
- // Wait for the Image (and any of its own nested slots) to finish
278
- // rendering into `container` before handing it to `ctx.replaceWith`.
279
- // Slot grafts `container` into the DOM via an imperative `ref` callback
280
- // that Preact's own diffing doesn't track (see docs/slot-dom-graft-race.md)
281
- // -- handing it over before it has content means it gets grafted empty,
282
- // then mutated again later outside Preact's bookkeeping, which is what
283
- // was corrupting Preact's internal DOM pointers and causing sporadic
284
- // `insertBefore` crashes after the preact 10.29 bump.
285
- await UI.render(Image, imageComponentProps)(container);
277
+ UI.render(Image, imageComponentProps)(container);
286
278
  ctx.replaceWith(container);
287
279
  };
288
280
  }
289
281
 
290
- export async function tryRenderAemAssetsImage(
282
+ export function tryRenderAemAssetsImage(
291
283
  ctx: RenderContext,
292
284
  config: AemAssetsImageSlotConfig,
293
- ): Promise<void> {
285
+ ): void {
294
286
  // Renders an equivalent of the default image.
295
- async function renderDefaultImage(): Promise<void> {
287
+ function renderDefaultImage(): void {
296
288
  const container = config.wrapper ?? document.createElement('div');
297
289
  const { imageProps, params } = config;
298
290
  const imageComponentProps: ImageProps = {
@@ -301,8 +293,7 @@ export async function tryRenderAemAssetsImage(
301
293
  height: params.height,
302
294
  };
303
295
 
304
- // See the comment in `makeAemAssetsImageSlot` above.
305
- await UI.render(Image, imageComponentProps)(container);
296
+ UI.render(Image, imageComponentProps)(container);
306
297
  ctx.replaceWith(container);
307
298
  }
308
299
 
@@ -310,7 +301,7 @@ export async function tryRenderAemAssetsImage(
310
301
 
311
302
  if (!assetsEnabled) {
312
303
  // No-op, render the default image.
313
- await renderDefaultImage();
304
+ renderDefaultImage();
314
305
  return;
315
306
  }
316
307
 
@@ -324,9 +315,9 @@ export async function tryRenderAemAssetsImage(
324
315
 
325
316
  if (!isAemAssetsUrl(assetsUrl)) {
326
317
  // Not an AEM Assets URL, so render the default image.
327
- await renderDefaultImage();
318
+ renderDefaultImage();
328
319
  return;
329
320
  }
330
321
 
331
- await makeAemAssetsImageSlot(config)(ctx);
322
+ makeAemAssetsImageSlot(config)(ctx);
332
323
  }
package/src/lib/slot.tsx CHANGED
@@ -146,21 +146,9 @@ export function useSlot<K, V extends HTMLElement>(
146
146
  });
147
147
  };
148
148
 
149
- const handleLifeCycleRenderRef = useRef<() => Promise<void>>();
150
-
151
149
  const _registerMethod = useCallback((cb: Function) => {
152
150
  if (typeof cb === 'function') {
153
151
  methodsRef.current.push(cb);
154
-
155
- // If the slot already finished its normal init-triggered render pass
156
- // (status is back to 'ready'), nothing else will flush this method.
157
- // This happens when an async `slot` callback doesn't `return`/`await`
158
- // its own async work before calling replaceWith/appendChild/etc. --
159
- // `handleLifeCycleInit` moves on before this method gets registered.
160
- // Flush it ourselves rather than silently dropping it.
161
- if (status.current === 'ready') {
162
- handleLifeCycleRenderRef.current?.();
163
- }
164
152
  } else {
165
153
  console.warn('Skipped: Invalid _registerMethod', cb);
166
154
  }
@@ -169,59 +157,20 @@ export function useSlot<K, V extends HTMLElement>(
169
157
  // @ts-ignore
170
158
  context._registerMethod = _registerMethod;
171
159
 
172
- // `onRender`-style registered methods are replayed on every
173
- // `handleLifeCycleRender` pass (see `_registerMethod`/`methodsRef` above),
174
- // and `handleLifeCycleRender` always resets `children` to the fallback
175
- // before replaying them -- so `replaceWith`/`appendChild`/`prependChild`
176
- // must genuinely re-apply their VNode on every replay, not just once.
177
- // Without caching, each replay would call `createElement` fresh, handing
178
- // Preact a brand new VNode (and new `ref` identity) for content that's
179
- // already grafted, forcing it to detach/reattach the `ref` for no reason.
180
- // Caching per `elem` means repeat calls return the *same* VNode object;
181
- // Preact's diff treats `oldVNode === newVNode` as an explicit "unchanged,
182
- // skip" signal, so it never re-touches the grafted subtree. See
183
- // docs/slot-dom-graft-race.md.
184
- const htmlElementVNodeCache = useMemo(
185
- () => new Map<HTMLElement, VNode<any>>(),
186
- // `contentTag` isn't read by the factory -- it's a deliberate cache-bust
187
- // key, so a `contentTag` change starts a fresh cache instead of handing
188
- // out VNodes built for the old wrapper tag.
189
- // eslint-disable-next-line react-hooks/exhaustive-deps
190
- [contentTag],
191
- );
192
-
193
160
  const _htmlElementToVNode = useCallback(
194
161
  (elem: HTMLElement) => {
195
- const cached = htmlElementVNodeCache.get(elem);
196
- if (cached) return cached;
197
-
198
- const vnode = createElement(
162
+ return createElement(
199
163
  contentTag,
200
164
  {
201
165
  'data-slot-html-element': elem.tagName.toLowerCase(),
202
166
  ref: (refElem: HTMLElement | null): void => {
203
- if (refElem) {
204
- refElem.appendChild(elem);
205
- return;
206
- }
207
-
208
- // Preact calls `ref` with `null` when this specific VNode is
209
- // detached -- either superseded by a different element at the
210
- // same position (a later replaceWith/appendChild/prependChild
211
- // call) or because the Slot unmounted entirely. Either way,
212
- // `elem` is no longer part of the tree, so drop it from the
213
- // cache now instead of holding onto it (and the DOM node it
214
- // wraps) for the Slot's remaining lifetime.
215
- htmlElementVNodeCache.delete(elem);
167
+ refElem?.appendChild(elem);
216
168
  },
217
169
  },
218
170
  null,
219
171
  );
220
-
221
- htmlElementVNodeCache.set(elem, vnode);
222
- return vnode;
223
172
  },
224
- [contentTag, htmlElementVNodeCache],
173
+ [contentTag],
225
174
  );
226
175
 
227
176
  // @ts-ignore
@@ -388,8 +337,6 @@ export function useSlot<K, V extends HTMLElement>(
388
337
  status.current = 'ready';
389
338
  }, [children, context, name, props, render, state]);
390
339
 
391
- handleLifeCycleRenderRef.current = handleLifeCycleRender;
392
-
393
340
  // Initialization
394
341
  const handleLifeCycleInit = useCallback(async () => {
395
342
  if (!callback) return;