@decocms/blocks 7.34.2 → 7.34.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decocms/blocks",
3
- "version": "7.34.2",
3
+ "version": "7.34.3",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=24"
@@ -10,6 +10,7 @@ import { registerSection, registerSectionsSync } from "./registry";
10
10
  import {
11
11
  type AsyncRenderingConfig,
12
12
  getAsyncRenderingConfig,
13
+ registerAlwaysDeferSections,
13
14
  registerEagerSections,
14
15
  registerNeverDeferSections,
15
16
  registerSeoSections,
@@ -24,6 +25,7 @@ import {
24
25
  export interface SectionMetaEntry {
25
26
  eager?: boolean;
26
27
  neverDefer?: boolean;
28
+ deferred?: boolean;
27
29
  cache?: string;
28
30
  layout?: boolean;
29
31
  sync?: boolean;
@@ -48,6 +50,7 @@ export function applySectionConventions(input: ApplySectionConventionsInput): vo
48
50
 
49
51
  const eagerSections: string[] = [];
50
52
  const neverDeferSections: string[] = [];
53
+ const alwaysDeferSections: string[] = [];
51
54
  const layoutSections: string[] = [];
52
55
  const seoSections: string[] = [];
53
56
  const cacheableSections: Record<string, CacheableSectionInput> = {};
@@ -55,6 +58,7 @@ export function applySectionConventions(input: ApplySectionConventionsInput): vo
55
58
  for (const [key, entry] of Object.entries(meta)) {
56
59
  if (entry.eager) eagerSections.push(key);
57
60
  if (entry.neverDefer) neverDeferSections.push(key);
61
+ if (entry.deferred) alwaysDeferSections.push(key);
58
62
  if (entry.layout) layoutSections.push(key);
59
63
  if (entry.seo) seoSections.push(key);
60
64
  if (entry.cache) cacheableSections[key] = entry.cache as CacheableSectionInput;
@@ -88,6 +92,10 @@ export function applySectionConventions(input: ApplySectionConventionsInput): vo
88
92
  registerNeverDeferSections(neverDeferSections);
89
93
  }
90
94
 
95
+ if (alwaysDeferSections.length > 0) {
96
+ registerAlwaysDeferSections(alwaysDeferSections);
97
+ }
98
+
91
99
  // Always initialize asyncConfig so CMS Lazy deferral (`respectCmsLazy`) is
92
100
  // active — this is what makes the admin ⚡ toggle the source of truth. Without
93
101
  // it, asyncConfig stays null, `useAsync` is false in resolveDecoPage, and
@@ -32,6 +32,7 @@ import {
32
32
  isEagerRequest,
33
33
  registerCommerceLoader,
34
34
  registerEagerSections,
35
+ registerAlwaysDeferSections,
35
36
  registerNeverDeferSections,
36
37
  resolveDecoPage,
37
38
  resolveDeferredSectionFull,
@@ -468,6 +469,22 @@ describe("shouldDeferSection — admin is the source of truth", () => {
468
469
  expect(shouldDeferSection(section, 5, mkCfg({ foldThreshold: 3 }), false)).toBe(true);
469
470
  expect(shouldDeferSection(section, 1, mkCfg({ foldThreshold: 3 }), false)).toBe(false);
470
471
  });
472
+
473
+ it("`export const deferred = true` defers one section even with respectCmsLazy off", () => {
474
+ const key = "site/sections/HeavyPLP.tsx";
475
+ registerAlwaysDeferSections([key]);
476
+ const section = { __resolveType: key };
477
+ // Not ⚡-wrapped and respectCmsLazy disabled — the per-section flag is the
478
+ // only thing forcing deferral here.
479
+ expect(shouldDeferSection(section, 0, mkCfg({ respectCmsLazy: false }), false)).toBe(true);
480
+ });
481
+
482
+ it("`export const deferred = true` still stays eager for bots (SEO)", () => {
483
+ const key = "site/sections/HeavyPLPBot.tsx";
484
+ registerAlwaysDeferSections([key]);
485
+ const section = { __resolveType: key };
486
+ expect(shouldDeferSection(section, 0, mkCfg({ respectCmsLazy: false }), true)).toBe(false);
487
+ });
471
488
  });
472
489
 
473
490
  // ---------------------------------------------------------------------------
@@ -243,6 +243,28 @@ function isNeverDeferSection(key: string): boolean {
243
243
  return (G.__deco.neverDeferSectionKeys as Set<string> | undefined)?.has(key) ?? false;
244
244
  }
245
245
 
246
+ // ---------------------------------------------------------------------------
247
+ // Permanent alwaysDefer section registry
248
+ // ---------------------------------------------------------------------------
249
+
250
+ /**
251
+ * Register sections that declared `export const deferred = true`.
252
+ * These sections are ALWAYS deferred (rendered client-side on scroll),
253
+ * independent of the global `respectCmsLazy` config — the per-section opt-in
254
+ * for async-rendering a single heavy section (e.g. a PLP grid) without
255
+ * turning on deferral site-wide. Bots stay eager via the `isBotReq`
256
+ * short-circuit in {@link shouldDeferSection}.
257
+ */
258
+ export function registerAlwaysDeferSections(keys: string[]): void {
259
+ G.__deco.alwaysDeferSectionKeys ??= new Set();
260
+ const set: Set<string> = G.__deco.alwaysDeferSectionKeys;
261
+ for (const k of keys) set.add(k);
262
+ }
263
+
264
+ function isAlwaysDeferSection(key: string): boolean {
265
+ return (G.__deco.alwaysDeferSectionKeys as Set<string> | undefined)?.has(key) ?? false;
266
+ }
267
+
246
268
  // ---------------------------------------------------------------------------
247
269
  // Deferred rawProps cache — keeps rawProps server-side to trim HTML payload
248
270
  // ---------------------------------------------------------------------------
@@ -1497,6 +1519,12 @@ export function shouldDeferSection(
1497
1519
  const finalKey = resolveFinalSectionKey(section, matcherCtx);
1498
1520
  if (!finalKey) return false;
1499
1521
 
1522
+ // ── PER-SECTION FORCE-DEFER (`export const deferred = true`) ───────────────
1523
+ // Explicit code-level opt-in to defer one section regardless of the global
1524
+ // `respectCmsLazy` config — lets a site async-render a single heavy section
1525
+ // without a site-wide skeleton sweep. Bots already returned eager above.
1526
+ if (isAlwaysDeferSection(finalKey)) return true;
1527
+
1500
1528
  // ── ADMIN IS THE SOURCE OF TRUTH ──────────────────────────────────────────
1501
1529
  // If the editor marked the section ⚡ (wrapped in CMS Lazy/Deferred at any
1502
1530
  // nesting level, including multivariate flags), it is deferred —