@builder.io/sdk-svelte 5.2.16 → 5.4.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.
Files changed (164) hide show
  1. package/README.md +4 -0
  2. package/lib/browser/blocks/symbol/symbol.helpers.d.ts +1 -0
  3. package/lib/browser/blocks/symbol/symbol.helpers.js +6 -0
  4. package/lib/browser/blocks/symbol/symbol.svelte +5 -1
  5. package/lib/browser/components/block/block.helpers.d.ts +1 -3
  6. package/lib/browser/components/block/block.helpers.js +31 -4
  7. package/lib/browser/components/block/block.svelte +14 -1
  8. package/lib/browser/components/builder-scripts.svelte +39 -0
  9. package/lib/browser/components/builder-scripts.svelte.d.ts +22 -0
  10. package/lib/browser/components/content/components/styles.helpers.d.ts +1 -1
  11. package/lib/browser/components/content/components/styles.helpers.js +1 -1
  12. package/lib/browser/components/content/content.helpers.d.ts +5 -1
  13. package/lib/browser/components/content/content.helpers.js +39 -2
  14. package/lib/browser/components/content/content.svelte +4 -17
  15. package/lib/browser/components/content-variants/content-variants.svelte +15 -8
  16. package/lib/browser/components/content-variants/content-variants.svelte.d.ts +5 -0
  17. package/lib/browser/components/content-variants/helpers.d.ts +6 -0
  18. package/lib/browser/components/content-variants/helpers.js +5 -0
  19. package/lib/browser/constants/builder-registered-components.d.ts +1 -0
  20. package/lib/browser/constants/builder-registered-components.js +40 -33
  21. package/lib/browser/constants/device-sizes.js +14 -2
  22. package/lib/browser/constants/sdk-version.d.ts +1 -1
  23. package/lib/browser/constants/sdk-version.js +1 -1
  24. package/lib/browser/context/builder-scripts.context.d.ts +7 -0
  25. package/lib/browser/context/builder-scripts.context.js +5 -0
  26. package/lib/browser/functions/evaluate/browser-runtime/browser.js +13 -1
  27. package/lib/browser/functions/evaluate/evaluate.js +21 -5
  28. package/lib/browser/functions/evaluate/node-runtime/node-runtime.js +114 -23
  29. package/lib/browser/functions/evaluate/safe-state-expression.d.ts +33 -0
  30. package/lib/browser/functions/evaluate/safe-state-expression.js +236 -0
  31. package/lib/browser/functions/evaluate/should-force-browser-runtime-in-node.js +3 -1
  32. package/lib/browser/functions/extract-localized-values.d.ts +4 -0
  33. package/lib/browser/functions/extract-localized-values.js +65 -26
  34. package/lib/browser/functions/get-block-properties.js +2 -1
  35. package/lib/browser/functions/get-processed-block.d.ts +0 -1
  36. package/lib/browser/functions/get-processed-block.js +45 -57
  37. package/lib/browser/functions/is-editing-or-previewing.d.ts +1 -0
  38. package/lib/browser/functions/is-editing-or-previewing.js +3 -0
  39. package/lib/browser/functions/set.d.ts +29 -1
  40. package/lib/browser/functions/set.js +85 -4
  41. package/lib/browser/helpers/bounded-cache.d.ts +4 -0
  42. package/lib/browser/helpers/bounded-cache.js +12 -0
  43. package/lib/browser/helpers/copy-plain-data.d.ts +6 -0
  44. package/lib/browser/helpers/copy-plain-data.js +40 -0
  45. package/lib/browser/index-helpers/blocks-exports.d.ts +1 -0
  46. package/lib/browser/index-helpers/blocks-exports.js +1 -0
  47. package/lib/edge/blocks/symbol/symbol.helpers.d.ts +1 -0
  48. package/lib/edge/blocks/symbol/symbol.helpers.js +6 -0
  49. package/lib/edge/blocks/symbol/symbol.svelte +5 -1
  50. package/lib/edge/components/block/block.helpers.d.ts +1 -3
  51. package/lib/edge/components/block/block.helpers.js +31 -4
  52. package/lib/edge/components/block/block.svelte +14 -1
  53. package/lib/edge/components/builder-scripts.svelte +39 -0
  54. package/lib/edge/components/builder-scripts.svelte.d.ts +22 -0
  55. package/lib/edge/components/content/components/styles.helpers.d.ts +1 -1
  56. package/lib/edge/components/content/components/styles.helpers.js +1 -1
  57. package/lib/edge/components/content/content.helpers.d.ts +5 -1
  58. package/lib/edge/components/content/content.helpers.js +39 -2
  59. package/lib/edge/components/content/content.svelte +4 -17
  60. package/lib/edge/components/content-variants/content-variants.svelte +15 -8
  61. package/lib/edge/components/content-variants/content-variants.svelte.d.ts +5 -0
  62. package/lib/edge/components/content-variants/helpers.d.ts +6 -0
  63. package/lib/edge/components/content-variants/helpers.js +5 -0
  64. package/lib/edge/constants/builder-registered-components.d.ts +1 -0
  65. package/lib/edge/constants/builder-registered-components.js +40 -33
  66. package/lib/edge/constants/device-sizes.js +14 -2
  67. package/lib/edge/constants/sdk-version.d.ts +1 -1
  68. package/lib/edge/constants/sdk-version.js +1 -1
  69. package/lib/edge/context/builder-scripts.context.d.ts +7 -0
  70. package/lib/edge/context/builder-scripts.context.js +5 -0
  71. package/lib/edge/functions/evaluate/browser-runtime/browser.js +13 -1
  72. package/lib/edge/functions/evaluate/evaluate.js +21 -5
  73. package/lib/edge/functions/evaluate/node-runtime/node-runtime.js +114 -23
  74. package/lib/edge/functions/evaluate/safe-state-expression.d.ts +33 -0
  75. package/lib/edge/functions/evaluate/safe-state-expression.js +236 -0
  76. package/lib/edge/functions/evaluate/should-force-browser-runtime-in-node.js +3 -1
  77. package/lib/edge/functions/extract-localized-values.d.ts +4 -0
  78. package/lib/edge/functions/extract-localized-values.js +65 -26
  79. package/lib/edge/functions/get-block-properties.js +2 -1
  80. package/lib/edge/functions/get-processed-block.d.ts +0 -1
  81. package/lib/edge/functions/get-processed-block.js +45 -57
  82. package/lib/edge/functions/is-editing-or-previewing.d.ts +1 -0
  83. package/lib/edge/functions/is-editing-or-previewing.js +3 -0
  84. package/lib/edge/functions/set.d.ts +29 -1
  85. package/lib/edge/functions/set.js +85 -4
  86. package/lib/edge/helpers/bounded-cache.d.ts +4 -0
  87. package/lib/edge/helpers/bounded-cache.js +12 -0
  88. package/lib/edge/helpers/copy-plain-data.d.ts +6 -0
  89. package/lib/edge/helpers/copy-plain-data.js +40 -0
  90. package/lib/edge/index-helpers/blocks-exports.d.ts +1 -0
  91. package/lib/edge/index-helpers/blocks-exports.js +1 -0
  92. package/lib/node/blocks/symbol/symbol.helpers.d.ts +1 -0
  93. package/lib/node/blocks/symbol/symbol.helpers.js +6 -0
  94. package/lib/node/blocks/symbol/symbol.svelte +5 -1
  95. package/lib/node/components/block/block.helpers.d.ts +1 -3
  96. package/lib/node/components/block/block.helpers.js +31 -4
  97. package/lib/node/components/block/block.svelte +14 -1
  98. package/lib/node/components/builder-scripts.svelte +39 -0
  99. package/lib/node/components/builder-scripts.svelte.d.ts +22 -0
  100. package/lib/node/components/content/components/styles.helpers.d.ts +1 -1
  101. package/lib/node/components/content/components/styles.helpers.js +1 -1
  102. package/lib/node/components/content/content.helpers.d.ts +5 -1
  103. package/lib/node/components/content/content.helpers.js +39 -2
  104. package/lib/node/components/content/content.svelte +4 -17
  105. package/lib/node/components/content-variants/content-variants.svelte +15 -8
  106. package/lib/node/components/content-variants/content-variants.svelte.d.ts +5 -0
  107. package/lib/node/components/content-variants/helpers.d.ts +6 -0
  108. package/lib/node/components/content-variants/helpers.js +5 -0
  109. package/lib/node/constants/builder-registered-components.d.ts +1 -0
  110. package/lib/node/constants/builder-registered-components.js +40 -33
  111. package/lib/node/constants/device-sizes.js +14 -2
  112. package/lib/node/constants/sdk-version.d.ts +1 -1
  113. package/lib/node/constants/sdk-version.js +1 -1
  114. package/lib/node/context/builder-scripts.context.d.ts +7 -0
  115. package/lib/node/context/builder-scripts.context.js +5 -0
  116. package/lib/node/functions/evaluate/browser-runtime/browser.js +13 -1
  117. package/lib/node/functions/evaluate/evaluate.js +21 -5
  118. package/lib/node/functions/evaluate/node-runtime/node-runtime.js +114 -23
  119. package/lib/node/functions/evaluate/safe-state-expression.d.ts +33 -0
  120. package/lib/node/functions/evaluate/safe-state-expression.js +236 -0
  121. package/lib/node/functions/evaluate/should-force-browser-runtime-in-node.js +3 -1
  122. package/lib/node/functions/extract-localized-values.d.ts +4 -0
  123. package/lib/node/functions/extract-localized-values.js +65 -26
  124. package/lib/node/functions/get-block-properties.js +2 -1
  125. package/lib/node/functions/get-processed-block.d.ts +0 -1
  126. package/lib/node/functions/get-processed-block.js +45 -57
  127. package/lib/node/functions/is-editing-or-previewing.d.ts +1 -0
  128. package/lib/node/functions/is-editing-or-previewing.js +3 -0
  129. package/lib/node/functions/set.d.ts +29 -1
  130. package/lib/node/functions/set.js +85 -4
  131. package/lib/node/helpers/bounded-cache.d.ts +4 -0
  132. package/lib/node/helpers/bounded-cache.js +12 -0
  133. package/lib/node/helpers/copy-plain-data.d.ts +6 -0
  134. package/lib/node/helpers/copy-plain-data.js +40 -0
  135. package/lib/node/index-helpers/blocks-exports.d.ts +1 -0
  136. package/lib/node/index-helpers/blocks-exports.js +1 -0
  137. package/package.json +1 -1
  138. package/src/blocks/symbol/symbol.helpers.ts +7 -0
  139. package/src/blocks/symbol/symbol.svelte +8 -1
  140. package/src/components/block/block.helpers.ts +38 -5
  141. package/src/components/block/block.svelte +14 -1
  142. package/src/components/builder-scripts.svelte +57 -0
  143. package/src/components/content/components/styles.helpers.ts +1 -1
  144. package/src/components/content/content.helpers.ts +45 -4
  145. package/src/components/content/content.svelte +7 -24
  146. package/src/components/content-variants/content-variants.svelte +21 -8
  147. package/src/components/content-variants/helpers.ts +6 -0
  148. package/src/constants/builder-registered-components.ts +40 -34
  149. package/src/constants/device-sizes.ts +14 -3
  150. package/src/constants/sdk-version.ts +1 -1
  151. package/src/context/builder-scripts.context.ts +6 -0
  152. package/src/functions/evaluate/browser-runtime/browser.ts +14 -1
  153. package/src/functions/evaluate/evaluate.ts +22 -5
  154. package/src/functions/evaluate/node-runtime/node-runtime.ts +116 -25
  155. package/src/functions/evaluate/safe-state-expression.ts +224 -0
  156. package/src/functions/evaluate/should-force-browser-runtime-in-node.ts +3 -1
  157. package/src/functions/extract-localized-values.ts +61 -27
  158. package/src/functions/get-block-properties.ts +5 -1
  159. package/src/functions/get-processed-block.ts +46 -57
  160. package/src/functions/is-editing-or-previewing.ts +3 -0
  161. package/src/functions/set.ts +89 -4
  162. package/src/helpers/bounded-cache.ts +12 -0
  163. package/src/helpers/copy-plain-data.ts +36 -0
  164. package/src/index-helpers/blocks-exports.ts +1 -0
@@ -0,0 +1,12 @@
1
+ export const createBoundedCache = (max) => {
2
+ const map = new Map();
3
+ return {
4
+ get: (key) => map.get(key),
5
+ set: (key, value) => {
6
+ if (!map.has(key) && map.size >= max) {
7
+ map.delete(map.keys().next().value);
8
+ }
9
+ map.set(key, value);
10
+ }
11
+ };
12
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Deep-copies plain objects and arrays (anything else is kept by reference), so
3
+ * that state writes made while rendering never reach the content object, which
4
+ * apps often cache and share between requests.
5
+ */
6
+ export declare const copyPlainData: <T>(value: T, seen?: WeakMap<object, any>) => T;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Deep-copies plain objects and arrays (anything else is kept by reference), so
3
+ * that state writes made while rendering never reach the content object, which
4
+ * apps often cache and share between requests.
5
+ */
6
+ export const copyPlainData = (value, seen = new WeakMap()) => {
7
+ if (value === null || typeof value !== 'object')
8
+ return value;
9
+ if (seen.has(value))
10
+ return seen.get(value);
11
+ if (Array.isArray(value)) {
12
+ const copy = [];
13
+ seen.set(value, copy);
14
+ for (let i = 0; i < value.length; i++) {
15
+ copy[i] = copyPlainData(value[i], seen);
16
+ }
17
+ return copy;
18
+ }
19
+ const proto = Object.getPrototypeOf(value);
20
+ if (proto !== Object.prototype && proto !== null)
21
+ return value;
22
+ const copy = {};
23
+ seen.set(value, copy);
24
+ for (const key of Object.keys(value)) {
25
+ const copied = copyPlainData(value[key], seen);
26
+ if (key === '__proto__') {
27
+ // Assigning would call the prototype setter; JSON can carry `__proto__` as an own key.
28
+ Object.defineProperty(copy, key, {
29
+ value: copied,
30
+ enumerable: true,
31
+ writable: true,
32
+ configurable: true
33
+ });
34
+ }
35
+ else {
36
+ copy[key] = copied;
37
+ }
38
+ }
39
+ return copy;
40
+ };
@@ -7,6 +7,7 @@ export { default as Symbol } from '../blocks/symbol/index';
7
7
  export { default as Text } from '../blocks/text/index';
8
8
  export { default as Video } from '../blocks/video/index';
9
9
  export { default as Blocks } from '../components/blocks/index';
10
+ export { default as BuilderScripts } from '../components/builder-scripts.svelte';
10
11
  export { default as Content } from '../components/content-variants/index';
11
12
  /**
12
13
  * Builder Context
@@ -7,6 +7,7 @@ export { default as Symbol } from '../blocks/symbol/index';
7
7
  export { default as Text } from '../blocks/text/index';
8
8
  export { default as Video } from '../blocks/video/index';
9
9
  export { default as Blocks } from '../components/blocks/index';
10
+ export { default as BuilderScripts } from '../components/builder-scripts.svelte';
10
11
  export { default as Content } from '../components/content-variants/index';
11
12
  /**
12
13
  * Builder Context
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@builder.io/sdk-svelte",
3
3
  "description": "Builder.io SDK for Svelte",
4
- "version": "5.2.16",
4
+ "version": "5.4.0",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "lib",
@@ -12,6 +12,13 @@ export interface SymbolInfo {
12
12
  ownerId?: string;
13
13
  global?: boolean;
14
14
  }
15
+ const isSymbolContentBinding = (key: string) => {
16
+ const path = key.startsWith('component.') ? key.slice(10) : key;
17
+ return path === 'options.symbol' || path === 'options.symbol.content' || path.startsWith('options.symbol.content.');
18
+ };
19
+
20
+ // Bound symbol content is evaluated at render time, so it is not in the parent content JSON.
21
+ export const isSymbolContentInlinedInParent = (symbol: SymbolInfo | undefined, bindings: Record<string, string> | undefined) => !!symbol?.content && !Object.keys(bindings || {}).some(isSymbolContentBinding);
15
22
  export const fetchSymbolContent = async ({
16
23
  builderContextValue,
17
24
  symbol
@@ -8,7 +8,10 @@
8
8
  import { getClassPropName } from "../../functions/get-class-prop-name";
9
9
  import type { Nullable } from "../../types/typescript";
10
10
  import { setAttrs } from "../helpers";
11
- import { fetchSymbolContent } from "./symbol.helpers";
11
+ import {
12
+ fetchSymbolContent,
13
+ isSymbolContentInlinedInParent,
14
+ } from "./symbol.helpers";
12
15
  import type { SymbolProps } from "./symbol.types";
13
16
 
14
17
  export let symbol: SymbolProps["symbol"] = undefined;
@@ -75,6 +78,10 @@
75
78
  <ContentVariants
76
79
  nonce={$builderContext.nonce}
77
80
  isNestedRender={true}
81
+ isContentInlinedInParent={isSymbolContentInlinedInParent(
82
+ symbol,
83
+ builderBlock?.bindings
84
+ )}
78
85
  apiVersion={$builderContext.apiVersion}
79
86
  apiKey={symbol?.global && symbol?.ownerId
80
87
  ? symbol.ownerId
@@ -3,6 +3,7 @@ import type { BuilderContextInterface, RegisteredComponent, RegisteredComponents
3
3
  import { evaluate } from '../../functions/evaluate/index';
4
4
  import { extractTextStyles } from '../../functions/extract-text-styles';
5
5
  import { getStyle } from '../../functions/get-style';
6
+ import { isEditingOrPreviewing } from '../../functions/is-editing-or-previewing';
6
7
  import type { BuilderBlock } from '../../types/builder-block';
7
8
  import type { RepeatData } from './types';
8
9
  const checkIsComponentRestricted = (component: RegisteredComponent | null | undefined, model: string) => {
@@ -34,6 +35,28 @@ export const getComponent = ({
34
35
  return ref;
35
36
  }
36
37
  };
38
+
39
+ // Reused per block so repeated items keep a stable block identity across renders.
40
+ const BLOCKS_WITHOUT_REPEAT = new WeakMap<BuilderBlock, BuilderBlock>();
41
+ const getBlockWithoutRepeat = (block: BuilderBlock) => {
42
+ if (isEditingOrPreviewing()) {
43
+ const {
44
+ repeat: _repeat,
45
+ ...rest
46
+ } = block;
47
+ return rest;
48
+ }
49
+ let blockWithoutRepeat = BLOCKS_WITHOUT_REPEAT.get(block);
50
+ if (!blockWithoutRepeat) {
51
+ const {
52
+ repeat: _repeat,
53
+ ...rest
54
+ } = block;
55
+ blockWithoutRepeat = rest;
56
+ BLOCKS_WITHOUT_REPEAT.set(block, blockWithoutRepeat);
57
+ }
58
+ return blockWithoutRepeat;
59
+ };
37
60
  export const getRepeatItemData = ({
38
61
  block,
39
62
  context
@@ -46,8 +69,7 @@ export const getRepeatItemData = ({
46
69
  * which will not work if there is a repeat.
47
70
  */
48
71
  const {
49
- repeat,
50
- ...blockWithoutRepeat
72
+ repeat
51
73
  } = block;
52
74
  if (!repeat?.collection) {
53
75
  return undefined;
@@ -64,6 +86,7 @@ export const getRepeatItemData = ({
64
86
  }
65
87
  const collectionName = repeat.collection.split('.').pop();
66
88
  const itemNameToUse = repeat.itemName || (collectionName ? collectionName + 'Item' : 'item');
89
+ const blockWithoutRepeat = getBlockWithoutRepeat(block);
67
90
  const repeatArray = itemsArray.map<RepeatData>((item, index) => ({
68
91
  context: {
69
92
  ...context,
@@ -101,11 +124,21 @@ export const provideLinkComponent = (block: RegisteredComponent | null | undefin
101
124
  };
102
125
  return {};
103
126
  };
127
+ const FILTERED_REGISTERED_COMPONENTS = new WeakMap<RegisteredComponents, Map<string, RegisteredComponents>>();
104
128
  export const provideRegisteredComponents = (block: RegisteredComponent | null | undefined, registeredComponents: RegisteredComponents, model: string) => {
105
129
  if (block?.shouldReceiveBuilderProps?.builderComponents) {
106
- const filteredRegisteredComponents = Object.fromEntries(Object.entries(registeredComponents).filter(([_, component]) => {
107
- return !checkIsComponentRestricted(component, model);
108
- }));
130
+ let byModel = FILTERED_REGISTERED_COMPONENTS.get(registeredComponents);
131
+ if (!byModel) {
132
+ byModel = new Map();
133
+ FILTERED_REGISTERED_COMPONENTS.set(registeredComponents, byModel);
134
+ }
135
+ let filteredRegisteredComponents = byModel.get(model);
136
+ if (!filteredRegisteredComponents) {
137
+ filteredRegisteredComponents = Object.fromEntries(Object.entries(registeredComponents).filter(([_, component]) => {
138
+ return !checkIsComponentRestricted(component, model);
139
+ }));
140
+ byModel.set(model, filteredRegisteredComponents);
141
+ }
109
142
  return {
110
143
  builderComponents: filteredRegisteredComponents
111
144
  };
@@ -103,7 +103,15 @@
103
103
  : [];
104
104
  };
105
105
  $: componentRefProps = () => {
106
- return {
106
+ const deps: unknown[] = [
107
+ processedBlock(),
108
+ $context,
109
+ $context.rootState,
110
+ $context.localState,
111
+ registeredComponents,
112
+ linkComponent,
113
+ ];
114
+ const componentRefProps: ComponentProps = {
107
115
  blockChildren: processedBlock().children ?? [],
108
116
  componentRef: blockComponent?.()?.component,
109
117
  componentOptions: {
@@ -124,12 +132,17 @@
124
132
  includeBlockProps: blockComponent?.()?.noWrap === true,
125
133
  isInteractive: !(blockComponent?.()?.isRSC && TARGET === "rsc"),
126
134
  };
135
+ return componentRefProps;
127
136
  };
128
137
 
129
138
  let _processedBlock = {
130
139
  value: null as BuilderBlock | null,
131
140
  update: false,
132
141
  };
142
+ let _componentRefProps = {
143
+ value: null as ComponentProps | null,
144
+ deps: [] as unknown[],
145
+ };
133
146
 
134
147
  onMount(() => {
135
148
  const blockId = processedBlock().id;
@@ -0,0 +1,57 @@
1
+ <script context="module" lang="ts">
2
+ type BuilderScriptsProps = {
3
+ nonce?: string;
4
+ children?: any;
5
+ };
6
+
7
+ /**
8
+ * Emits the Variant Container and A/B test helper scripts once for all `Content` components inside it.
9
+ */
10
+ </script>
11
+
12
+ <script lang="ts">
13
+ import { getContext, setContext } from "svelte";
14
+
15
+ import {
16
+ getInitPersonalizationVariantsFnsScriptString,
17
+ SDKS_SUPPORTING_PERSONALIZATION,
18
+ } from "../blocks/personalization-container/helpers";
19
+ import { TARGET } from "../constants/target";
20
+ import BuilderScriptsContext from "../context/builder-scripts.context";
21
+ import { isBrowser } from "../functions/is-browser";
22
+ import {
23
+ getInitVariantsFnsScriptString,
24
+ SDKS_SUPPORTING_BUILDER_SCRIPTS,
25
+ } from "./content-variants/helpers";
26
+ import InlinedScript from "./inlined-script.svelte";
27
+
28
+ export let nonce: BuilderScriptsProps["nonce"] = undefined;
29
+
30
+ let parentScriptsContext = getContext(BuilderScriptsContext.key);
31
+
32
+ let isSupported = SDKS_SUPPORTING_BUILDER_SCRIPTS.includes(TARGET);
33
+ let shouldRenderAbTestFns = TARGET !== "react" || !isBrowser();
34
+
35
+ setContext(BuilderScriptsContext.key, {
36
+ scriptsEmitted: isSupported,
37
+ });
38
+ </script>
39
+
40
+ {#if isSupported && !parentScriptsContext?.scriptsEmitted}
41
+ {#if SDKS_SUPPORTING_PERSONALIZATION.includes(TARGET)}
42
+ <InlinedScript
43
+ id="builderio-init-personalization-variants-fns"
44
+ nonce={nonce || ""}
45
+ scriptStr={getInitPersonalizationVariantsFnsScriptString()}
46
+ />
47
+ {/if}
48
+
49
+ {#if shouldRenderAbTestFns}
50
+ <InlinedScript
51
+ id="builderio-init-variants-fns"
52
+ nonce={nonce || ""}
53
+ scriptStr={getInitVariantsFnsScriptString()}
54
+ />
55
+ {/if}
56
+ {/if}
57
+ <slot />
@@ -76,7 +76,7 @@ export const getCss = ({
76
76
  return cssCode?.replace(/&/g, `div[builder-content-id="${contentId}"]`) || '';
77
77
  };
78
78
  const DEFAULT_STYLES = `
79
- .builder-button {
79
+ :where(.builder-button) {
80
80
  all: unset;
81
81
  }
82
82
 
@@ -1,19 +1,60 @@
1
- import type { BuilderRenderState } from '../../context/types';
1
+ import type { BuilderRenderState, RegisteredComponent, RegisteredComponents } from '../../context/types';
2
+ import { serializeIncludingFunctions } from '../../functions/register-component';
3
+ import { copyPlainData } from '../../helpers/copy-plain-data';
2
4
  import type { BuilderContent } from '../../types/builder-content';
3
- import type { Nullable } from '../../types/typescript';
5
+ import type { ComponentInfo } from '../../types/components';
6
+ import type { Dictionary, Nullable } from '../../types/typescript';
4
7
  import type { ContentProps } from './content.types';
8
+
9
+ // Registrations are usually module constants, so their derived forms are built once per object.
10
+ const SERIALIZED_COMPONENT_INFOS = new WeakMap<object, ComponentInfo>();
11
+ const REGISTERED_COMPONENT_ENTRIES = new WeakMap<object, RegisteredComponent>();
12
+ const getSerializedComponentInfo = (registration: RegisteredComponent): ComponentInfo => {
13
+ const cached = SERIALIZED_COMPONENT_INFOS.get(registration);
14
+ if (cached) return cached;
15
+ const {
16
+ component: _,
17
+ ...rest
18
+ } = registration;
19
+ const info: ComponentInfo = serializeIncludingFunctions((rest as ComponentInfo));
20
+ SERIALIZED_COMPONENT_INFOS.set(registration, info);
21
+ return info;
22
+ };
23
+ export const getRegisteredComponents = (registrations: RegisteredComponent[], wrapComponent: (component: any) => any): RegisteredComponents => {
24
+ const registeredComponents: RegisteredComponents = {};
25
+ for (const registration of registrations) {
26
+ let entry = REGISTERED_COMPONENT_ENTRIES.get(registration);
27
+ if (!entry) {
28
+ entry = ({
29
+ component: wrapComponent(registration.component),
30
+ ...getSerializedComponentInfo(registration)
31
+ } as RegisteredComponent);
32
+ REGISTERED_COMPONENT_ENTRIES.set(registration, entry);
33
+ }
34
+ registeredComponents[registration.name] = entry;
35
+ }
36
+ return registeredComponents;
37
+ };
38
+ export const getComponentInfos = (registrations: RegisteredComponent[]): Dictionary<ComponentInfo> => {
39
+ const componentInfos: Dictionary<ComponentInfo> = {};
40
+ for (const registration of registrations) {
41
+ componentInfos[registration.name] = getSerializedComponentInfo(registration);
42
+ }
43
+ return componentInfos;
44
+ };
5
45
  export const getRootStateInitialValue = ({
6
46
  content,
7
47
  data,
8
48
  locale
9
49
  }: Pick<ContentProps, 'content' | 'data' | 'locale'>) => {
10
50
  const defaultValues: BuilderRenderState = {};
11
- const initialState = content?.data?.state || {};
51
+ const seen = new WeakMap<object, any>();
52
+ const initialState = copyPlainData(content?.data?.state || {}, seen);
12
53
 
13
54
  // set default values for content state inputs
14
55
  content?.data?.inputs?.forEach(input => {
15
56
  if (input.name && input.defaultValue !== undefined) {
16
- defaultValues[input.name] = input.defaultValue;
57
+ defaultValues[input.name] = copyPlainData(input.defaultValue, seen);
17
58
  }
18
59
  });
19
60
  return {
@@ -8,13 +8,9 @@
8
8
  import type {
9
9
  BuilderContextInterface,
10
10
  BuilderRenderState,
11
- RegisteredComponents,
12
11
  } from "../../context/types";
13
12
  import { evaluate } from "../../functions/evaluate/evaluate";
14
- import { serializeIncludingFunctions } from "../../functions/register-component";
15
13
  import { logger } from "../../helpers/logger";
16
- import type { ComponentInfo } from "../../types/components";
17
- import type { Dictionary } from "../../types/typescript";
18
14
  import Blocks from "../blocks/blocks.svelte";
19
15
  import { getUpdateVariantVisibilityScript } from "../content-variants/helpers";
20
16
  import DynamicDiv from "../dynamic-div.svelte";
@@ -22,7 +18,9 @@
22
18
  import EnableEditor from "./components/enable-editor.svelte";
23
19
  import ContentStyles from "./components/styles.svelte";
24
20
  import {
21
+ getComponentInfos,
25
22
  getContentInitialValue,
23
+ getRegisteredComponents,
26
24
  getRootStateInitialValue,
27
25
  } from "./content.helpers";
28
26
  import type { ContentProps } from "./content.types";
@@ -62,18 +60,9 @@
62
60
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain
63
61
  contentId: content?.id!,
64
62
  });
65
- let registeredComponents = [
66
- ...getDefaultRegisteredComponents(),
67
- ...(customComponents || []),
68
- ].reduce<RegisteredComponents>(
69
- (acc, { component, ...info }) => ({
70
- ...acc,
71
- [info.name]: {
72
- component: component,
73
- ...serializeIncludingFunctions(info),
74
- },
75
- }),
76
- {}
63
+ let registeredComponents = getRegisteredComponents(
64
+ [...getDefaultRegisteredComponents(), ...(customComponents || [])],
65
+ (component) => component
77
66
  );
78
67
  let builderContextSignal = writable({
79
68
  content: getContentInitialValue({
@@ -91,16 +80,10 @@
91
80
  canTrack: canTrack,
92
81
  apiKey: apiKey,
93
82
  apiVersion: apiVersion,
94
- componentInfos: [
83
+ componentInfos: getComponentInfos([
95
84
  ...getDefaultRegisteredComponents(),
96
85
  ...(customComponents || []),
97
- ].reduce<Dictionary<ComponentInfo>>(
98
- (acc, { component: _, ...info }) => ({
99
- ...acc,
100
- [info.name]: serializeIncludingFunctions(info),
101
- }),
102
- {}
103
- ),
86
+ ]),
104
87
  inheritedStyles: {},
105
88
  BlocksWrapper: blocksWrapper || "div",
106
89
  BlocksWrapperProps: blocksWrapperProps || {},
@@ -4,11 +4,15 @@
4
4
  * For internal use only. Do not provide this prop.
5
5
  */
6
6
  isNestedRender?: boolean;
7
+ /**
8
+ * For internal use only. Do not provide this prop.
9
+ */
10
+ isContentInlinedInParent?: boolean;
7
11
  };
8
12
  </script>
9
13
 
10
14
  <script lang="ts">
11
- import { onMount } from "svelte";
15
+ import { getContext, onMount } from "svelte";
12
16
 
13
17
  import {
14
18
  SDKS_SUPPORTING_PERSONALIZATION,
@@ -16,6 +20,7 @@
16
20
  hasPersonalizationContainer,
17
21
  } from "../../blocks/personalization-container/helpers";
18
22
  import { TARGET } from "../../constants/target";
23
+ import BuilderScriptsContext from "../../context/builder-scripts.context";
19
24
  import { handleABTestingSync } from "../../helpers/ab-tests";
20
25
  import { getDefaultCanTrack } from "../../helpers/canTrack";
21
26
  import ContentComponent from "../content/content.svelte";
@@ -31,6 +36,8 @@
31
36
 
32
37
  export let canTrack: VariantsProviderProps["canTrack"] = undefined;
33
38
  export let content: VariantsProviderProps["content"];
39
+ export let isContentInlinedInParent: VariantsProviderProps["isContentInlinedInParent"] =
40
+ undefined;
34
41
  export let nonce: VariantsProviderProps["nonce"] = undefined;
35
42
  export let apiHost: VariantsProviderProps["apiHost"] = undefined;
36
43
  export let isNestedRender: VariantsProviderProps["isNestedRender"] =
@@ -55,6 +62,8 @@
55
62
  undefined;
56
63
  export let trustedHosts: VariantsProviderProps["trustedHosts"] = undefined;
57
64
 
65
+ let scriptsContext = getContext(BuilderScriptsContext.key);
66
+
58
67
  $: updateCookieAndStylesScriptStr = () => {
59
68
  return getUpdateCookieAndStylesScript(
60
69
  getVariants(content).map((value) => ({
@@ -86,7 +95,9 @@
86
95
  content: content,
87
96
  });
88
97
  let shouldEmitPersonalizationFns =
89
- TARGET !== "reactNative" && hasPersonalizationContainer(content);
98
+ TARGET !== "reactNative" &&
99
+ !isContentInlinedInParent &&
100
+ hasPersonalizationContainer(content);
90
101
 
91
102
  onMount(() => {
92
103
  /**
@@ -96,7 +107,7 @@
96
107
  });
97
108
  </script>
98
109
 
99
- {#if shouldEmitPersonalizationFns}
110
+ {#if shouldEmitPersonalizationFns && !!!scriptsContext?.scriptsEmitted}
100
111
  {#if SDKS_SUPPORTING_PERSONALIZATION.includes(TARGET)}
101
112
  <InlinedScript
102
113
  id="builderio-init-personalization-variants-fns"
@@ -107,11 +118,13 @@
107
118
  {/if}
108
119
 
109
120
  {#if shouldRenderVariants}
110
- <InlinedScript
111
- id="builderio-init-variants-fns"
112
- scriptStr={getInitVariantsFnsScriptString()}
113
- nonce={nonce || ""}
114
- />
121
+ {#if !!!scriptsContext?.scriptsEmitted}
122
+ <InlinedScript
123
+ id="builderio-init-variants-fns"
124
+ scriptStr={getInitVariantsFnsScriptString()}
125
+ nonce={nonce || ""}
126
+ />
127
+ {/if}
115
128
  <InlinedStyles
116
129
  id="builderio-variants"
117
130
  styles={hideVariantsStyleString()}
@@ -13,6 +13,12 @@ import { UPDATE_COOKIES_AND_STYLES_SCRIPT, UPDATE_VARIANT_VISIBILITY_SCRIPT } fr
13
13
  */
14
14
  const UPDATE_COOKIES_AND_STYLES_SCRIPT_NAME = 'builderIoAbTest';
15
15
  const UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME = 'builderIoRenderContent';
16
+
17
+ /**
18
+ * SDKs where `BuilderScripts` can dedupe helper scripts. Elsewhere `Content` cannot read its context
19
+ * (RSC server components, Angular DI) or has no inline scripts (React Native).
20
+ */
21
+ export const SDKS_SUPPORTING_BUILDER_SCRIPTS = (['react', 'vue', 'svelte', 'solid', 'qwik'] as Target[]);
16
22
  export const getVariants = (content: Nullable<BuilderContent>) => Object.values(content?.variations || {}).map(variant => ({
17
23
  ...variant,
18
24
  testVariationId: variant.id,
@@ -24,42 +24,48 @@ import { default as Text } from '../blocks/text/index';
24
24
  import type { RegisteredComponent } from '../context/types';
25
25
  import { getExtraComponents } from './extra-components';
26
26
  import { TARGET } from './target';
27
+ let DEFAULT_REGISTERED_COMPONENTS: RegisteredComponent[] | undefined;
27
28
 
28
29
  /**
29
30
  * Returns a list of all registered components.
30
31
  * NOTE: This needs to be a function to work around ESM circular dependencies.
32
+ * The list is built once, on first use, so its entries keep a stable identity.
31
33
  */
32
- export const getDefaultRegisteredComponents: () => RegisteredComponent[] = () => [{
33
- component: Button,
34
- ...buttonComponentInfo
35
- }, {
36
- component: Columns,
37
- ...columnsComponentInfo
38
- }, {
39
- component: Fragment,
40
- ...fragmentComponentInfo
41
- }, {
42
- component: Image,
43
- ...imageComponentInfo
44
- }, {
45
- component: Section,
46
- ...sectionComponentInfo
47
- }, {
48
- component: Slot,
49
- ...slotComponentInfo
50
- }, {
51
- component: Symbol,
52
- ...symbolComponentInfo
53
- }, {
54
- component: Text,
55
- ...textComponentInfo
56
- }, ...(SDKS_SUPPORTING_PERSONALIZATION.includes(TARGET) ? [{
57
- component: PersonalizationContainer,
58
- ...personalizationContainerComponentInfo
59
- }] : []), ...(TARGET === 'rsc' ? [] : [{
60
- component: Tabs,
61
- ...tabsComponentInfo
62
- }, {
63
- component: Accordion,
64
- ...accordionComponentInfo
65
- }]), ...getExtraComponents()]
34
+ export const getDefaultRegisteredComponents: () => RegisteredComponent[] = () => {
35
+ if (DEFAULT_REGISTERED_COMPONENTS) return DEFAULT_REGISTERED_COMPONENTS;
36
+ DEFAULT_REGISTERED_COMPONENTS = [{
37
+ component: Button,
38
+ ...buttonComponentInfo
39
+ }, {
40
+ component: Columns,
41
+ ...columnsComponentInfo
42
+ }, {
43
+ component: Fragment,
44
+ ...fragmentComponentInfo
45
+ }, {
46
+ component: Image,
47
+ ...imageComponentInfo
48
+ }, {
49
+ component: Section,
50
+ ...sectionComponentInfo
51
+ }, {
52
+ component: Slot,
53
+ ...slotComponentInfo
54
+ }, {
55
+ component: Symbol,
56
+ ...symbolComponentInfo
57
+ }, {
58
+ component: Text,
59
+ ...textComponentInfo
60
+ }, ...(SDKS_SUPPORTING_PERSONALIZATION.includes(TARGET) ? [{
61
+ component: PersonalizationContainer,
62
+ ...personalizationContainerComponentInfo
63
+ }] : []), ...(TARGET === 'rsc' ? [] : [{
64
+ component: Tabs,
65
+ ...tabsComponentInfo
66
+ }, {
67
+ component: Accordion,
68
+ ...accordionComponentInfo
69
+ }]), ...getExtraComponents()];
70
+ return DEFAULT_REGISTERED_COMPONENTS;
71
+ }
@@ -1,4 +1,3 @@
1
- import { fastClone } from '../functions/fast-clone';
2
1
  export type SizeName = 'large' | 'medium' | 'small' | 'xsmall';
3
2
  interface Size {
4
3
  min: number;
@@ -34,8 +33,20 @@ interface Breakpoints {
34
33
  medium?: number;
35
34
  }
36
35
  export const getSizesForBreakpoints = (breakpoints: Breakpoints) => {
37
- const newSizes = fastClone(SIZES); // Note: this helps to get a deep clone of fields like small, medium etc
38
-
36
+ const newSizes: Record<SizeName, Size> = {
37
+ xsmall: {
38
+ ...SIZES.xsmall
39
+ },
40
+ small: {
41
+ ...SIZES.small
42
+ },
43
+ medium: {
44
+ ...SIZES.medium
45
+ },
46
+ large: {
47
+ ...SIZES.large
48
+ }
49
+ };
39
50
  if (!breakpoints) {
40
51
  return newSizes;
41
52
  }
@@ -1 +1 @@
1
- export const SDK_VERSION = "5.2.16"
1
+ export const SDK_VERSION = "5.4.0"
@@ -0,0 +1,6 @@
1
+ const key = Symbol();
2
+
3
+ export default {
4
+ BuilderScripts: { scriptsEmitted: false },
5
+ key,
6
+ };