@builder.io/react 2.0.4-0 → 2.0.4-11

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 (47) hide show
  1. package/dist/builder-react-lite.cjs.js +1 -15
  2. package/dist/builder-react-lite.cjs.js.map +1 -1
  3. package/dist/builder-react-lite.esm.js +1 -15
  4. package/dist/builder-react-lite.esm.js.map +1 -1
  5. package/dist/builder-react.browser.js +1 -24
  6. package/dist/builder-react.browser.js.map +1 -1
  7. package/dist/builder-react.cjs.js +1 -15
  8. package/dist/builder-react.cjs.js.map +1 -1
  9. package/dist/builder-react.es5.js +1 -15
  10. package/dist/builder-react.es5.js.map +1 -1
  11. package/dist/builder-react.unpkg.js +1 -24
  12. package/dist/builder-react.unpkg.js.map +1 -1
  13. package/dist/lib/package.json +4 -2
  14. package/dist/lib/src/blocks/Embed.js.map +1 -1
  15. package/dist/lib/src/blocks/Section.js +2 -0
  16. package/dist/lib/src/blocks/Section.js.map +1 -1
  17. package/dist/lib/src/blocks/Symbol.js +2 -2
  18. package/dist/lib/src/blocks/Symbol.js.map +1 -1
  19. package/dist/lib/src/components/builder-block.component.js +3 -10
  20. package/dist/lib/src/components/builder-block.component.js.map +1 -1
  21. package/dist/lib/src/components/builder-blocks.component.js.map +1 -1
  22. package/dist/lib/src/components/builder-component.component.js +16 -6
  23. package/dist/lib/src/components/builder-component.component.js.map +1 -1
  24. package/dist/lib/src/components/builder-content.component.js +3 -3
  25. package/dist/lib/src/components/builder-content.component.js.map +1 -1
  26. package/dist/lib/src/constants/device-sizes.constant.js.map +1 -1
  27. package/dist/lib/src/functions/apply-patch-with-mutation.js +1 -1
  28. package/dist/lib/src/functions/apply-patch-with-mutation.js.map +1 -1
  29. package/dist/lib/src/functions/apply-patch-with-mutation.test.js +54 -0
  30. package/dist/lib/src/functions/apply-patch-with-mutation.test.js.map +1 -0
  31. package/dist/lib/src/scripts/init-editing.js +1 -1
  32. package/dist/types/src/blocks/Mutation.d.ts +1 -0
  33. package/dist/types/src/blocks/Slot.d.ts +1 -0
  34. package/dist/types/src/blocks/raw/RawText.d.ts +1 -0
  35. package/dist/types/src/components/builder-component.component.d.ts +6 -0
  36. package/dist/types/src/functions/apply-patch-with-mutation.d.ts +2 -2
  37. package/dist/types/src/functions/apply-patch-with-mutation.test.d.ts +1 -0
  38. package/package.json +4 -2
  39. package/src/blocks/Section.tsx +16 -11
  40. package/src/blocks/Symbol.tsx +1 -0
  41. package/src/components/builder-block.component.tsx +7 -13
  42. package/src/components/builder-blocks.component.tsx +10 -8
  43. package/src/components/builder-component.component.tsx +36 -21
  44. package/src/components/builder-content.component.tsx +3 -4
  45. package/src/functions/apply-patch-with-mutation.test.ts +55 -0
  46. package/src/functions/apply-patch-with-mutation.ts +5 -5
  47. package/src/scripts/init-editing.ts +1 -1
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ var apply_patch_with_mutation_1 = require("./apply-patch-with-mutation");
4
+ describe('applyPatchWithMinimalMutationChain', function () {
5
+ test('Basic shallow update', function () {
6
+ var obj = {
7
+ foo: 'bar',
8
+ };
9
+ var patch = {
10
+ op: 'replace',
11
+ path: '/foo',
12
+ value: '60px',
13
+ };
14
+ var applied = (0, apply_patch_with_mutation_1.applyPatchWithMinimalMutationChain)(obj, patch);
15
+ expect(applied.foo).toBe('60px');
16
+ expect(applied).not.toBe(obj);
17
+ });
18
+ test('Deep object updates', function () {
19
+ var obj = {
20
+ foo: {
21
+ bar: true,
22
+ },
23
+ baz: {},
24
+ };
25
+ var patch = {
26
+ op: 'replace',
27
+ path: '/foo/bar',
28
+ value: '60px',
29
+ };
30
+ var applied = (0, apply_patch_with_mutation_1.applyPatchWithMinimalMutationChain)(obj, patch);
31
+ expect(applied.foo.bar).toBe('60px');
32
+ expect(applied).not.toBe(obj);
33
+ expect(applied.foo).not.toBe(obj.foo);
34
+ expect(applied.baz).toBe(obj.baz);
35
+ });
36
+ test('Deep array updates', function () {
37
+ var obj = {
38
+ foo: [{ bar: true }],
39
+ baz: {},
40
+ };
41
+ var patch = {
42
+ op: 'replace',
43
+ path: '/foo/0/bar',
44
+ value: '60px',
45
+ };
46
+ var applied = (0, apply_patch_with_mutation_1.applyPatchWithMinimalMutationChain)(obj, patch);
47
+ expect(applied.foo[0].bar).toBe('60px');
48
+ expect(applied).not.toBe(obj);
49
+ expect(applied.foo).not.toBe(obj.foo);
50
+ expect(applied.foo[0]).not.toBe(obj.foo[0]);
51
+ expect(applied.baz).toBe(obj.baz);
52
+ });
53
+ });
54
+ //# sourceMappingURL=apply-patch-with-mutation.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-patch-with-mutation.test.js","sourceRoot":"","sources":["../../../../src/functions/apply-patch-with-mutation.test.ts"],"names":[],"mappings":";;AAAA,yEAAiF;AAEjF,QAAQ,CAAC,oCAAoC,EAAE;IAC7C,IAAI,CAAC,sBAAsB,EAAE;QAC3B,IAAM,GAAG,GAAG;YACV,GAAG,EAAE,KAAK;SACX,CAAC;QACF,IAAM,KAAK,GAAG;YACZ,EAAE,EAAE,SAAS;YACb,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,MAAM;SACL,CAAC;QACX,IAAM,OAAO,GAAG,IAAA,8DAAkC,EAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC/D,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,qBAAqB,EAAE;QAC1B,IAAM,GAAG,GAAG;YACV,GAAG,EAAE;gBACH,GAAG,EAAE,IAAI;aACV;YACD,GAAG,EAAE,EAAE;SACR,CAAC;QACF,IAAM,KAAK,GAAG;YACZ,EAAE,EAAE,SAAS;YACb,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,MAAM;SACL,CAAC;QACX,IAAM,OAAO,GAAG,IAAA,8DAAkC,EAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC/D,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACtC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,oBAAoB,EAAE;QACzB,IAAM,GAAG,GAAG;YACV,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;YACpB,GAAG,EAAE,EAAE;SACR,CAAC;QACF,IAAM,KAAK,GAAG;YACZ,EAAE,EAAE,SAAS;YACb,IAAI,EAAE,YAAY;YAClB,KAAK,EAAE,MAAM;SACL,CAAC;QAEX,IAAM,OAAO,GAAG,IAAA,8DAAkC,EAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC/D,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACtC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -7,7 +7,7 @@ if (typeof window !== 'undefined') {
7
7
  type: 'builder.isReactSdk',
8
8
  data: {
9
9
  value: true,
10
- supportsPatchUpdates: 'v3',
10
+ supportsPatchUpdates: 'v4',
11
11
  priorVersion: package_json_1.version,
12
12
  },
13
13
  }, '*');
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { BuilderElement } from '@builder.io/sdk';
2
3
  declare type MutationProps = {
3
4
  selector: string;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  declare type DropzoneProps = {
2
3
  name: string;
3
4
  };
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { BuilderElement } from '@builder.io/sdk';
2
3
  export interface RawTextProps {
3
4
  attributes?: any;
@@ -181,6 +181,10 @@ export interface BuilderComponentProps {
181
181
  * navigation to other pages unintended
182
182
  */
183
183
  stopClickPropagationWhenEditing?: boolean;
184
+ /**
185
+ * Locale code, should match one of the locales in your spaces locale, wll auto resolve the localized inputs to the localized value
186
+ */
187
+ locale?: string;
184
188
  }
185
189
  export interface BuilderComponentState {
186
190
  state: any;
@@ -247,6 +251,7 @@ export declare class BuilderComponent extends React.Component<BuilderComponentPr
247
251
  url?: string | undefined;
248
252
  isChild?: boolean | undefined;
249
253
  stopClickPropagationWhenEditing?: boolean | undefined;
254
+ locale?: string | undefined;
250
255
  children?: React.ReactNode;
251
256
  };
252
257
  get name(): string | undefined;
@@ -295,6 +300,7 @@ export declare class BuilderComponent extends React.Component<BuilderComponentPr
295
300
  checkStyles(data: any): void;
296
301
  reload(): void;
297
302
  get content(): Content | undefined;
303
+ get externalState(): any;
298
304
  get useContent(): any;
299
305
  render(): JSX.Element;
300
306
  evalExpression(expression: string): string;
@@ -1,5 +1,5 @@
1
- export declare const applyPatchWithMinimalMutationChain: (obj: any, patch: {
1
+ export declare const applyPatchWithMinimalMutationChain: <T extends object>(obj: T, patch: {
2
2
  path: string;
3
3
  op: 'add' | 'remove' | 'replace';
4
4
  value: any;
5
- }, preserveRoot?: boolean) => any;
5
+ }, preserveRoot?: boolean) => T;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@builder.io/react",
3
- "version": "2.0.4-0",
3
+ "version": "2.0.4-11",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "main": "dist/builder-react.cjs.js",
@@ -75,6 +75,7 @@
75
75
  "@types/rollup-plugin-commonjs": "^9.2.0",
76
76
  "@types/rollup-plugin-json": "^3.0.3",
77
77
  "@types/rollup-plugin-node-resolve": "^4.1.0",
78
+ "@types/traverse": "^0.6.32",
78
79
  "colors": "^1.1.2",
79
80
  "commitizen": "^4.2.4",
80
81
  "coveralls": "^3.0.0",
@@ -123,10 +124,11 @@
123
124
  "react-dom": ">=16.8.0"
124
125
  },
125
126
  "dependencies": {
126
- "@builder.io/sdk": "^1.1.27",
127
+ "@builder.io/sdk": "^1.1.28-2",
127
128
  "@emotion/core": "^10.0.17",
128
129
  "hash-sum": "^2.0.0",
129
130
  "preact": "^10.1.0",
131
+ "traverse": "^0.6.6",
130
132
  "vm2": "^3.9.10"
131
133
  },
132
134
  "gitHead": "4d96fbc32864698afbb355ab991c6d90be991951"
@@ -40,18 +40,23 @@ class SectionComponent extends React.Component<SectionProps, { inView?: boolean
40
40
  if (typeof IntersectionObserver === 'undefined' || !this.ref) {
41
41
  this.setState({ inView: true });
42
42
  } else {
43
- const observer = new IntersectionObserver((entries, observer) => {
44
- entries.forEach(entry => {
45
- if (entry.intersectionRatio > 0) {
46
- this.setState({
47
- inView: true,
48
- });
49
- if (this.ref) {
50
- observer.unobserve(this.ref);
43
+ const observer = new IntersectionObserver(
44
+ (entries, observer) => {
45
+ entries.forEach(entry => {
46
+ if (entry.intersectionRatio > 0) {
47
+ this.setState({
48
+ inView: true,
49
+ });
50
+ if (this.ref) {
51
+ observer.unobserve(this.ref);
52
+ }
51
53
  }
52
- }
53
- });
54
- });
54
+ });
55
+ },
56
+ {
57
+ rootMargin: '10px',
58
+ }
59
+ );
55
60
 
56
61
  observer.observe(this.ref);
57
62
 
@@ -121,6 +121,7 @@ class SymbolComponent extends React.Component<SymbolProps> {
121
121
  this.placeholder
122
122
  ) : (
123
123
  <BuilderComponent
124
+ {...(state.state?.locale && { locale: state.state.locale })}
124
125
  isChild
125
126
  ref={(ref: any) => (this.ref = ref)}
126
127
  context={{ ...state.context, symbolId: this.props.builderBlock?.id }}
@@ -18,8 +18,8 @@ const camelCaseToKebabCase = (str?: string) =>
18
18
  const kebabCaseToCamelCase = (str = '') =>
19
19
  str.replace(/-([a-z])/g, match => match[1].toUpperCase());
20
20
 
21
+
21
22
  const Device = { desktop: 0, tablet: 1, mobile: 2 };
22
- const blocksMap: Record<string, BuilderElement> = {};
23
23
 
24
24
  const voidElements = new Set([
25
25
  'area',
@@ -136,7 +136,7 @@ export class BuilderBlock extends React.Component<
136
136
  }
137
137
 
138
138
  get block() {
139
- return blocksMap[this.props.block.id!] || this.props.block;
139
+ return this.props.block;
140
140
  }
141
141
 
142
142
  get emotionCss() {
@@ -249,17 +249,11 @@ export class BuilderBlock extends React.Component<
249
249
  }
250
250
 
251
251
  if (location.href.includes('builder.debug=true')) {
252
- this.eval('debugger');
252
+ eval('debugger');
253
253
  }
254
- let newBlock = { ...this.block };
255
254
  for (const patch of patches) {
256
- // TODO: soehow mark this.block as a new object,
257
- // e.g. access it's parent hm. maybe do the listning mutations
258
- // on hte parent element not the child (or rather
259
- // send the message to the parent)
260
- applyPatchWithMinimalMutationChain(newBlock, patch);
255
+ applyPatchWithMinimalMutationChain(this.props.block, patch, true);
261
256
  }
262
- blocksMap[this.props.block.id!] = newBlock;
263
257
  this.setState({ updates: this.state.updates + 1 });
264
258
 
265
259
  break;
@@ -460,9 +454,9 @@ export class BuilderBlock extends React.Component<
460
454
  }
461
455
 
462
456
  const innerComponentProperties = (options.component || options.options) && {
463
- ...options.options,
464
- ...(options.component.options || options.component.data),
465
- };
457
+ ...options.options,
458
+ ...(options.component.options || options.component.data),
459
+ };
466
460
 
467
461
  const isVoid = voidElements.has(TagName);
468
462
 
@@ -108,14 +108,16 @@ export class BuilderBlocks extends React.Component<BuilderBlocksProps, BuilderBl
108
108
  // TODO: only fi in iframe?
109
109
  builder-path={Builder.isIframe ? this.path : undefined}
110
110
  builder-parent-id={this.parentId}
111
- css={{
112
- ...(!this.props.emailMode && {
113
- display: 'flex',
114
- flexDirection: 'column',
115
- alignItems: 'stretch',
116
- }),
117
- ...this.props.style,
118
- } as any}
111
+ css={
112
+ {
113
+ ...(!this.props.emailMode && {
114
+ display: 'flex',
115
+ flexDirection: 'column',
116
+ alignItems: 'stretch',
117
+ }),
118
+ ...this.props.style,
119
+ } as any
120
+ }
119
121
  onClick={() => {
120
122
  if (this.noBlocks) {
121
123
  this.onClickEmptyBlocks();
@@ -98,7 +98,6 @@ const sizeMap = {
98
98
  mobile: 'small',
99
99
  };
100
100
 
101
-
102
101
  const fetchCache: { [key: string]: any } = {};
103
102
 
104
103
  export interface BuilderComponentProps {
@@ -277,6 +276,11 @@ export interface BuilderComponentProps {
277
276
  * navigation to other pages unintended
278
277
  */
279
278
  stopClickPropagationWhenEditing?: boolean;
279
+
280
+ /**
281
+ * Locale code, should match one of the locales in your spaces locale, wll auto resolve the localized inputs to the localized value
282
+ */
283
+ locale?: string;
280
284
  }
281
285
 
282
286
  export interface BuilderComponentState {
@@ -841,7 +845,7 @@ export class BuilderComponent extends React.Component<
841
845
  get data() {
842
846
  const data = {
843
847
  ...(this.inlinedContent && this.inlinedContent.data?.state),
844
- ...this.props.data,
848
+ ...this.externalState,
845
849
  ...this.state.state,
846
850
  };
847
851
  Object.assign(this.rootState, data);
@@ -852,7 +856,7 @@ export class BuilderComponent extends React.Component<
852
856
  // TODO: shallow diff
853
857
  if (this.props.data && prevProps.data !== this.props.data) {
854
858
  this.state.update((state: any) => {
855
- Object.assign(state, this.props.data);
859
+ Object.assign(state, this.externalState);
856
860
  });
857
861
  }
858
862
 
@@ -892,6 +896,13 @@ export class BuilderComponent extends React.Component<
892
896
  return content;
893
897
  }
894
898
 
899
+ get externalState() {
900
+ return {
901
+ ...this.props.data,
902
+ ...(this.props.locale ? { locale: this.props.locale } : {}),
903
+ };
904
+ }
905
+
895
906
  get useContent() {
896
907
  return this.content || this.state.context.builderContent;
897
908
  }
@@ -900,7 +911,10 @@ export class BuilderComponent extends React.Component<
900
911
  const content = this.content;
901
912
 
902
913
  const dataString =
903
- Builder.isBrowser && this.props.data && size(this.props.data) && hash(this.props.data);
914
+ Builder.isBrowser &&
915
+ this.externalState &&
916
+ size(this.externalState) &&
917
+ hash(this.externalState);
904
918
  let key = Builder.isEditing ? this.name : this.props.entry;
905
919
  if (key && !Builder.isEditing && dataString && dataString.length < 300) {
906
920
  key += ':' + dataString;
@@ -971,6 +985,7 @@ export class BuilderComponent extends React.Component<
971
985
  !this.isPreviewing && { initialContent: [] }),
972
986
  ...(this.props.url && { url: this.props.url }),
973
987
  ...this.props.options,
988
+ ...(this.props.locale ? { locale: this.props.locale } : {}),
974
989
  ...(this.options.codegen && {
975
990
  format: 'react',
976
991
  }),
@@ -1272,7 +1287,7 @@ export class BuilderComponent extends React.Component<
1272
1287
  deviceSize: this.deviceSizeState,
1273
1288
  device: this.device,
1274
1289
  ...data.state,
1275
- ...this.props.data,
1290
+ ...this.externalState,
1276
1291
  }),
1277
1292
  };
1278
1293
  if (this.mounted) {
@@ -1358,22 +1373,22 @@ export class BuilderComponent extends React.Component<
1358
1373
  const builderModelRe = /builder\.io\/api\/v2\/([^\/\?]+)/i;
1359
1374
  const builderModelMatch = url.match(builderModelRe);
1360
1375
  const model = builderModelMatch && builderModelMatch[1];
1361
- this.handleRequest(key, finalUrl);
1362
- const currentSubscription = this.httpSubscriptionPerKey[key];
1363
- if (currentSubscription) {
1364
- currentSubscription.unsubscribe();
1365
- }
1366
-
1367
- // TODO: fix this
1368
- const newSubscription = (this.httpSubscriptionPerKey[key] =
1369
- this.onStateChange.subscribe(() => {
1370
- const newUrl = this.evalExpression(url);
1371
- if (newUrl !== finalUrl) {
1372
- this.handleRequest(key, newUrl);
1373
- this.lastHttpRequests[key] = newUrl;
1374
- }
1375
- }));
1376
- this.subscriptions.add(newSubscription);
1376
+ this.handleRequest(key, finalUrl);
1377
+ const currentSubscription = this.httpSubscriptionPerKey[key];
1378
+ if (currentSubscription) {
1379
+ currentSubscription.unsubscribe();
1380
+ }
1381
+
1382
+ // TODO: fix this
1383
+ const newSubscription = (this.httpSubscriptionPerKey[key] =
1384
+ this.onStateChange.subscribe(() => {
1385
+ const newUrl = this.evalExpression(url);
1386
+ if (newUrl !== finalUrl) {
1387
+ this.handleRequest(key, newUrl);
1388
+ this.lastHttpRequests[key] = newUrl;
1389
+ }
1390
+ }));
1391
+ this.subscriptions.add(newSubscription);
1377
1392
  } else {
1378
1393
  this.handleRequest(key, this.evalExpression(url));
1379
1394
  }
@@ -140,18 +140,17 @@ export class BuilderContent<ContentType extends object = any> extends React.Comp
140
140
  if (location.href.includes('builder.debug=true')) {
141
141
  eval('debugger');
142
142
  }
143
- let newData;
143
+ let newData = this.state.data as any;
144
144
  for (const patch of patches) {
145
- newData = applyPatchWithMinimalMutationChain(this.state.data, patch, false);
145
+ newData = applyPatchWithMinimalMutationChain(newData, patch, false);
146
146
  }
147
147
  this.setState({
148
148
  updates: this.state.updates + 1,
149
149
  data: newData,
150
150
  });
151
151
  if (this.props.contentLoaded) {
152
- this.props.contentLoaded(newData, this.state.data);
152
+ this.props.contentLoaded(newData.data, newData);
153
153
  }
154
-
155
154
  break;
156
155
  }
157
156
  }
@@ -0,0 +1,55 @@
1
+ import { applyPatchWithMinimalMutationChain } from './apply-patch-with-mutation';
2
+
3
+ describe('applyPatchWithMinimalMutationChain', () => {
4
+ test('Basic shallow update', () => {
5
+ const obj = {
6
+ foo: 'bar',
7
+ };
8
+ const patch = {
9
+ op: 'replace',
10
+ path: '/foo',
11
+ value: '60px',
12
+ } as const;
13
+ const applied = applyPatchWithMinimalMutationChain(obj, patch);
14
+ expect(applied.foo).toBe('60px');
15
+ expect(applied).not.toBe(obj);
16
+ });
17
+
18
+ test('Deep object updates', () => {
19
+ const obj = {
20
+ foo: {
21
+ bar: true,
22
+ },
23
+ baz: {},
24
+ };
25
+ const patch = {
26
+ op: 'replace',
27
+ path: '/foo/bar',
28
+ value: '60px',
29
+ } as const;
30
+ const applied = applyPatchWithMinimalMutationChain(obj, patch);
31
+ expect(applied.foo.bar).toBe('60px');
32
+ expect(applied).not.toBe(obj);
33
+ expect(applied.foo).not.toBe(obj.foo);
34
+ expect(applied.baz).toBe(obj.baz);
35
+ });
36
+
37
+ test('Deep array updates', () => {
38
+ const obj = {
39
+ foo: [{ bar: true }],
40
+ baz: {},
41
+ };
42
+ const patch = {
43
+ op: 'replace',
44
+ path: '/foo/0/bar',
45
+ value: '60px',
46
+ } as const;
47
+
48
+ const applied = applyPatchWithMinimalMutationChain(obj, patch);
49
+ expect(applied.foo[0].bar).toBe('60px');
50
+ expect(applied).not.toBe(obj);
51
+ expect(applied.foo).not.toBe(obj.foo);
52
+ expect(applied.foo[0]).not.toBe(obj.foo[0]);
53
+ expect(applied.baz).toBe(obj.baz);
54
+ });
55
+ });
@@ -1,8 +1,8 @@
1
- export const applyPatchWithMinimalMutationChain = (
2
- obj: any,
1
+ export const applyPatchWithMinimalMutationChain = <T extends object>(
2
+ obj: T,
3
3
  patch: { path: string; op: 'add' | 'remove' | 'replace'; value: any },
4
- preserveRoot = true
5
- ) => {
4
+ preserveRoot = false
5
+ ): T => {
6
6
  if (Object(obj) !== obj) {
7
7
  return obj;
8
8
  }
@@ -13,7 +13,7 @@ export const applyPatchWithMinimalMutationChain = (
13
13
  }
14
14
 
15
15
  const newObj = preserveRoot ? obj : { ...obj };
16
- let objPart = newObj;
16
+ let objPart = newObj as any;
17
17
  for (let i = 0; i < pathArr.length; i++) {
18
18
  const isLast = i === pathArr.length - 1;
19
19
  const property = pathArr[i];
@@ -6,7 +6,7 @@ if (typeof window !== 'undefined') {
6
6
  type: 'builder.isReactSdk',
7
7
  data: {
8
8
  value: true,
9
- supportsPatchUpdates: 'v3',
9
+ supportsPatchUpdates: 'v4',
10
10
  priorVersion: version,
11
11
  },
12
12
  },