@builder.io/mitosis 0.11.4 → 0.11.5

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.
@@ -4,6 +4,7 @@ import { TranspilerArgs } from '../../types/transpiler';
4
4
  import { BuilderContent, BuilderElement } from '@builder.io/sdk';
5
5
  type InternalOptions = {
6
6
  skipMapper?: boolean;
7
+ includeStateMap?: boolean;
7
8
  };
8
9
  export declare const blockToBuilder: (json: MitosisNode, options?: ToBuilderOptions, _internalOptions?: InternalOptions) => BuilderElement;
9
10
  export declare const componentToBuilder: (options?: ToBuilderOptions) => ({ component }: TranspilerArgs) => BuilderContent;
@@ -19,6 +19,7 @@ const replace_identifiers_1 = require("../../helpers/replace-identifiers");
19
19
  const state_1 = require("../../helpers/state");
20
20
  const builder_1 = require("../../parsers/builder");
21
21
  const symbol_processor_1 = require("../../symbols/symbol-processor");
22
+ const mitosis_node_1 = require("../../types/mitosis-node");
22
23
  const core_1 = require("@babel/core");
23
24
  const generator_1 = __importDefault(require("@babel/generator"));
24
25
  const parser_1 = require("@babel/parser");
@@ -27,6 +28,91 @@ const lodash_1 = require("lodash");
27
28
  const legacy_1 = __importDefault(require("neotraverse/legacy"));
28
29
  const standalone_1 = require("prettier/standalone");
29
30
  const on_mount_1 = require("../helpers/on-mount");
31
+ const isValidCollection = (code) => {
32
+ if (!code || typeof code !== 'string') {
33
+ return false;
34
+ }
35
+ // Pattern: Array.from({ length: number })
36
+ // Examples: "Array.from({ length: 10 })", "Array.from({ length: 5 })"
37
+ const arrayFromPattern = /^Array\.from\(\{\s*length\s*:\s*\d+\s*\}\)$/;
38
+ if (arrayFromPattern.test(code)) {
39
+ return false;
40
+ }
41
+ // Pattern: alphanumeric strings separated by dots
42
+ // Examples: "abc.def", "state.list1", "data.items"
43
+ const dotPattern = /^[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*$/;
44
+ return dotPattern.test(code);
45
+ };
46
+ const replaceWithStateVariable = (code, stateMap) => {
47
+ if (!code) {
48
+ return '';
49
+ }
50
+ if (stateMap === null || stateMap === void 0 ? void 0 : stateMap.has(code)) {
51
+ return 'state.' + (stateMap.get(code) || '');
52
+ }
53
+ return code;
54
+ };
55
+ const generateUniqueKey = (state) => {
56
+ let newKeyPrefix = 'dataBuilderList';
57
+ let counter = 1;
58
+ while (state[newKeyPrefix + counter]) {
59
+ counter++;
60
+ }
61
+ return newKeyPrefix + counter;
62
+ };
63
+ const convertMitosisStateToBuilderState = (state) => {
64
+ return Object.entries(state).reduce((acc, [key, value]) => {
65
+ if ((value === null || value === void 0 ? void 0 : value.type) === 'property' && (value === null || value === void 0 ? void 0 : value.code)) {
66
+ if (value.code === 'true' || value.code === 'false') {
67
+ acc[key] = value.code === 'true';
68
+ }
69
+ else if (value.code === 'null') {
70
+ acc[key] = null;
71
+ }
72
+ else if (value.code === 'undefined') {
73
+ acc[key] = undefined;
74
+ }
75
+ else if (!Number.isNaN(Number(value.code))) {
76
+ acc[key] = Number(value.code);
77
+ }
78
+ else {
79
+ try {
80
+ acc[key] = JSON.parse(value.code);
81
+ }
82
+ catch (e) {
83
+ acc[key] = value.code;
84
+ }
85
+ }
86
+ }
87
+ return acc;
88
+ }, {});
89
+ };
90
+ const findStateWithinMitosisNode = (node, options, state, stateMap) => {
91
+ var _a, _b, _c;
92
+ if ((0, mitosis_node_1.checkIsForNode)(node)) {
93
+ if (!isValidCollection((_a = node.bindings.each) === null || _a === void 0 ? void 0 : _a.code) &&
94
+ !stateMap.has((_b = node.bindings.each) === null || _b === void 0 ? void 0 : _b.code)) {
95
+ const newKey = generateUniqueKey(state);
96
+ const code = (_c = node.bindings.each) === null || _c === void 0 ? void 0 : _c.code;
97
+ try {
98
+ state[newKey] = JSON.parse(code);
99
+ stateMap.set(code, newKey);
100
+ }
101
+ catch (parseError) {
102
+ // The parsing error happens when `code` is a function or expression
103
+ // We would need `eval` to parse the code and then set the state. But because
104
+ // of security concerns we are not handling this case right now.
105
+ // Will revisit this if we need to support this.
106
+ console.log('Failed to parse:', code, parseError);
107
+ }
108
+ }
109
+ }
110
+ node.children.forEach((child) => findStateWithinMitosisNode(child, options, state, stateMap));
111
+ };
112
+ const findStateWithinMitosisComponent = (component, options, state, stateMap) => {
113
+ component.children.forEach((child) => findStateWithinMitosisNode(child, options, state, stateMap));
114
+ return state;
115
+ };
30
116
  const omitMetaProperties = (obj) => (0, lodash_1.omitBy)(obj, (_value, key) => key.startsWith('$'));
31
117
  const builderBlockPrefixes = ['Amp', 'Core', 'Builder', 'Raw', 'Form'];
32
118
  const mapComponentName = (name) => {
@@ -137,8 +223,9 @@ const componentMappers = {
137
223
  return block;
138
224
  },
139
225
  For(_node, options) {
140
- var _a;
226
+ var _a, _b, _c;
141
227
  const node = _node;
228
+ const stateMap = options.stateMap;
142
229
  const replaceIndexNode = (str) => (0, replace_identifiers_1.replaceNodes)({
143
230
  code: str,
144
231
  nodeMaps: [
@@ -203,7 +290,9 @@ const componentMappers = {
203
290
  name: 'Core:Fragment',
204
291
  },
205
292
  repeat: {
206
- collection: (_a = node.bindings.each) === null || _a === void 0 ? void 0 : _a.code,
293
+ collection: isValidCollection((_a = node.bindings.each) === null || _a === void 0 ? void 0 : _a.code)
294
+ ? ((_b = node.bindings.each) === null || _b === void 0 ? void 0 : _b.code) || ''
295
+ : replaceWithStateVariable((_c = node.bindings.each) === null || _c === void 0 ? void 0 : _c.code, stateMap),
207
296
  itemName: node.scope.forName,
208
297
  },
209
298
  children: node.children
@@ -611,9 +700,29 @@ const blockToBuilder = (json, options = {}, _internalOptions = {}) => {
611
700
  return processLocalizedValues(element, json);
612
701
  };
613
702
  exports.blockToBuilder = blockToBuilder;
703
+ const recursivelyCheckForChildrenWithSameComponent = (elementOrContent, componentName, path = '') => {
704
+ var _a, _b, _c, _d, _e;
705
+ if ((0, builder_1.isBuilderElement)(elementOrContent)) {
706
+ if (((_a = elementOrContent.component) === null || _a === void 0 ? void 0 : _a.name) === componentName) {
707
+ return path;
708
+ }
709
+ return (((_b = elementOrContent.children) === null || _b === void 0 ? void 0 : _b.map((child, index) => recursivelyCheckForChildrenWithSameComponent(child, componentName, `${path}.children[${index}]`)).find(Boolean)) || '');
710
+ }
711
+ if ((_c = elementOrContent.data) === null || _c === void 0 ? void 0 : _c.blocks) {
712
+ return (((_e = (_d = elementOrContent.data) === null || _d === void 0 ? void 0 : _d.blocks) === null || _e === void 0 ? void 0 : _e.map((block, index) => recursivelyCheckForChildrenWithSameComponent(block, componentName, `${path ? `${path}.` : ''}data.blocks[${index}]`)).find(Boolean)) || '');
713
+ }
714
+ return '';
715
+ };
716
+ function removeItem(obj, path, indexToRemove) {
717
+ return (0, lodash_1.set)((0, lodash_1.cloneDeep)(obj), // Clone to ensure immutability
718
+ path, (0, lodash_1.filter)((0, lodash_1.get)(obj, path), (item, index) => index !== indexToRemove));
719
+ }
614
720
  const componentToBuilder = (options = {}) => ({ component }) => {
615
- var _a, _b;
721
+ var _a, _b, _c;
616
722
  const hasState = (0, state_1.checkHasState)(component);
723
+ if (!options.stateMap) {
724
+ options.stateMap = new Map();
725
+ }
617
726
  const result = (0, fast_clone_1.fastClone)({
618
727
  data: {
619
728
  httpRequests: (_b = (_a = component === null || component === void 0 ? void 0 : component.meta) === null || _a === void 0 ? void 0 : _a.useMetadata) === null || _b === void 0 ? void 0 : _b.httpRequests,
@@ -636,11 +745,19 @@ const componentToBuilder = (options = {}) => ({ component }) => {
636
745
  })`}
637
746
  `),
638
747
  cssCode: component === null || component === void 0 ? void 0 : component.style,
748
+ ...(() => {
749
+ const stateData = findStateWithinMitosisComponent(component, options, { ...convertMitosisStateToBuilderState(component.state) }, options.stateMap);
750
+ console.log('stateData', stateData);
751
+ return { state: stateData };
752
+ })(),
639
753
  blocks: component.children
640
754
  .filter(filter_empty_text_nodes_1.filterEmptyTextNodes)
641
755
  .map((child) => (0, exports.blockToBuilder)(child, options)),
642
756
  },
643
757
  });
758
+ if (((_c = result.data) === null || _c === void 0 ? void 0 : _c.state) && Object.keys(result.data.state).length === 0) {
759
+ delete result.data.state;
760
+ }
644
761
  const subComponentMap = {};
645
762
  for (const subComponent of component.subComponents) {
646
763
  const name = subComponent.name;
@@ -649,11 +766,33 @@ const componentToBuilder = (options = {}) => ({ component }) => {
649
766
  });
650
767
  }
651
768
  (0, legacy_1.default)([result, subComponentMap]).forEach(function (el) {
652
- var _a;
653
- if ((0, builder_1.isBuilderElement)(el)) {
654
- const value = subComponentMap[(_a = el.component) === null || _a === void 0 ? void 0 : _a.name];
769
+ var _a, _b, _c, _d, _e;
770
+ if ((0, builder_1.isBuilderElement)(el) && !((_a = el.meta) === null || _a === void 0 ? void 0 : _a.preventRecursion)) {
771
+ const value = subComponentMap[(_b = el.component) === null || _b === void 0 ? void 0 : _b.name];
655
772
  if (value) {
656
- (0, lodash_1.set)(el, 'component.options.symbol.content', value);
773
+ // Recursive Components are handled in the following steps :
774
+ // 1. Find out the path in which the component is self-referenced ( where that component reoccurs within it’s tree ).
775
+ // 2. We populate that component recursively for 4 times in a row.
776
+ // 3. Finally remove the recursive part from the last component which was populated.
777
+ // Also note that it doesn’t mean that component will render that many times, the rendering logic depends on the logic in it's parent. (Eg. show property binding)
778
+ const path = recursivelyCheckForChildrenWithSameComponent(value, (_c = el.component) === null || _c === void 0 ? void 0 : _c.name);
779
+ if (path) {
780
+ let tempElement = el;
781
+ for (let i = 0; i < 4; i++) {
782
+ const tempValue = (0, lodash_1.cloneDeep)(value);
783
+ (0, lodash_1.set)(tempElement, 'component.options.symbol.content', tempValue);
784
+ (0, lodash_1.set)(tempElement, 'meta.preventRecursion', true);
785
+ tempElement = (0, lodash_1.get)(tempValue, path);
786
+ }
787
+ // Finally remove the recursive part.
788
+ const arrayPath = path.replace(/\[\d+\]$/, '');
789
+ const newValue = removeItem(value, arrayPath, Number((_d = path.match(/\[(\d+)\]$/)) === null || _d === void 0 ? void 0 : _d[1]));
790
+ (0, lodash_1.set)(tempElement, 'component.options.symbol.content', newValue);
791
+ (0, lodash_1.set)(tempElement, 'meta.preventRecursion', true);
792
+ }
793
+ else {
794
+ (0, lodash_1.set)(el, 'component.options.symbol.content', value);
795
+ }
657
796
  }
658
797
  if (el.bindings) {
659
798
  for (const [key, value] of Object.entries(el.bindings)) {
@@ -670,6 +809,12 @@ const componentToBuilder = (options = {}) => ({ component }) => {
670
809
  }
671
810
  }
672
811
  }
812
+ if ((0, builder_1.isBuilderElement)(el) && ((_e = el.meta) === null || _e === void 0 ? void 0 : _e.preventRecursion)) {
813
+ delete el.meta.preventRecursion;
814
+ if (el.meta && Object.keys(el.meta).length === 0) {
815
+ delete el.meta;
816
+ }
817
+ }
673
818
  });
674
819
  return result;
675
820
  };
@@ -1,5 +1,6 @@
1
1
  import { BaseTranspilerOptions } from '../../types/transpiler';
2
2
  export interface ToBuilderOptions extends BaseTranspilerOptions {
3
3
  includeIds?: boolean;
4
+ stateMap?: Map<string, string>;
4
5
  }
5
6
  export type BuilderMetadata = {};
@@ -39,6 +39,15 @@ export declare function extractStateHook(code: string): {
39
39
  code: string;
40
40
  state: MitosisState;
41
41
  };
42
+ /**
43
+ * Extracts Mitosis state from Builder state.
44
+ * @param mitosisState Mitosis state to update
45
+ * @param builderState Builder state to extract from
46
+ * @returns
47
+ */
48
+ export declare function extractMitosisStateFromBuilderState(mitosisState: MitosisState, builderState?: {
49
+ [key: string]: any;
50
+ }): void;
42
51
  export declare function convertExportDefaultToReturn(code: string): string;
43
52
  export declare const createBuilderElement: (options?: Partial<BuilderElement>) => BuilderElement;
44
53
  export declare const isBuilderElement: (el: unknown) => el is BuilderElement;
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.builderContentToMitosisComponent = exports.isBuilderElement = exports.createBuilderElement = exports.convertExportDefaultToReturn = exports.extractStateHook = exports.getMetaFromBlock = exports.builderElementToMitosisNode = exports.symbolBlocksAsChildren = exports.getStyleStringFromBlock = void 0;
29
+ exports.builderContentToMitosisComponent = exports.isBuilderElement = exports.createBuilderElement = exports.convertExportDefaultToReturn = exports.extractMitosisStateFromBuilderState = exports.extractStateHook = exports.getMetaFromBlock = exports.builderElementToMitosisNode = exports.symbolBlocksAsChildren = exports.getStyleStringFromBlock = void 0;
30
30
  const symbol_processor_1 = require("../../symbols/symbol-processor");
31
31
  const babel = __importStar(require("@babel/core"));
32
32
  const generator_1 = __importDefault(require("@babel/generator"));
@@ -981,6 +981,34 @@ function extractStateHook(code) {
981
981
  return { code: newCode, state };
982
982
  }
983
983
  exports.extractStateHook = extractStateHook;
984
+ /**
985
+ * Extracts Mitosis state from Builder state.
986
+ * @param mitosisState Mitosis state to update
987
+ * @param builderState Builder state to extract from
988
+ * @returns
989
+ */
990
+ function extractMitosisStateFromBuilderState(mitosisState, builderState) {
991
+ if (!builderState)
992
+ return;
993
+ for (const key in builderState) {
994
+ let value = builderState[key];
995
+ if (typeof value === 'function' && !mitosisState[key]) {
996
+ mitosisState[key] = {
997
+ type: 'function',
998
+ code: value.toString(),
999
+ };
1000
+ continue;
1001
+ }
1002
+ if (!mitosisState[key]) {
1003
+ mitosisState[key] = {
1004
+ type: 'property',
1005
+ propertyType: 'normal',
1006
+ code: JSON.stringify(value),
1007
+ };
1008
+ }
1009
+ }
1010
+ }
1011
+ exports.extractMitosisStateFromBuilderState = extractMitosisStateFromBuilderState;
984
1012
  function convertExportDefaultToReturn(code) {
985
1013
  try {
986
1014
  const { types } = babel;
@@ -1082,7 +1110,7 @@ exports.createBuilderElement = createBuilderElement;
1082
1110
  const isBuilderElement = (el) => (el === null || el === void 0 ? void 0 : el['@type']) === '@builder.io/sdk:Element';
1083
1111
  exports.isBuilderElement = isBuilderElement;
1084
1112
  const builderContentPartToMitosisComponent = (builderContent, options = {}) => {
1085
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
1113
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
1086
1114
  builderContent = (0, fast_clone_1.fastClone)(builderContent);
1087
1115
  (0, legacy_1.default)(builderContent).forEach(function (elem) {
1088
1116
  var _a, _b;
@@ -1126,16 +1154,17 @@ const builderContentPartToMitosisComponent = (builderContent, options = {}) => {
1126
1154
  ...state,
1127
1155
  ...(0, helpers_1.mapBuilderContentStateToMitosisState)(((_c = builderContent.data) === null || _c === void 0 ? void 0 : _c.state) || {}),
1128
1156
  };
1157
+ extractMitosisStateFromBuilderState(mitosisState, (_d = builderContent.data) === null || _d === void 0 ? void 0 : _d.state);
1129
1158
  const componentJson = (0, create_mitosis_component_1.createMitosisComponent)({
1130
1159
  meta: {
1131
1160
  useMetadata: {
1132
- httpRequests: (_d = builderContent.data) === null || _d === void 0 ? void 0 : _d.httpRequests,
1161
+ httpRequests: (_e = builderContent.data) === null || _e === void 0 ? void 0 : _e.httpRequests,
1133
1162
  },
1134
1163
  // cmp.meta.cssCode exists for backwards compatibility, prefer cmp.style
1135
- ...(((_e = builderContent.data) === null || _e === void 0 ? void 0 : _e.cssCode) && { cssCode: builderContent.data.cssCode }),
1164
+ ...(((_f = builderContent.data) === null || _f === void 0 ? void 0 : _f.cssCode) && { cssCode: builderContent.data.cssCode }),
1136
1165
  },
1137
- ...(((_f = builderContent.data) === null || _f === void 0 ? void 0 : _f.cssCode) && { style: (_g = builderContent.data) === null || _g === void 0 ? void 0 : _g.cssCode }),
1138
- inputs: (_j = (_h = builderContent.data) === null || _h === void 0 ? void 0 : _h.inputs) === null || _j === void 0 ? void 0 : _j.map((input) => ({
1166
+ ...(((_g = builderContent.data) === null || _g === void 0 ? void 0 : _g.cssCode) && { style: (_h = builderContent.data) === null || _h === void 0 ? void 0 : _h.cssCode }),
1167
+ inputs: (_k = (_j = builderContent.data) === null || _j === void 0 ? void 0 : _j.inputs) === null || _k === void 0 ? void 0 : _k.map((input) => ({
1139
1168
  name: input.name,
1140
1169
  defaultValue: input.defaultValue,
1141
1170
  })),
@@ -1149,7 +1178,7 @@ const builderContentPartToMitosisComponent = (builderContent, options = {}) => {
1149
1178
  : []),
1150
1179
  ],
1151
1180
  },
1152
- children: (((_k = builderContent.data) === null || _k === void 0 ? void 0 : _k.blocks) || [])
1181
+ children: (((_l = builderContent.data) === null || _l === void 0 ? void 0 : _l.blocks) || [])
1153
1182
  .filter((item) => {
1154
1183
  var _a, _b;
1155
1184
  if ((_b = (_a = item.properties) === null || _a === void 0 ? void 0 : _a.src) === null || _b === void 0 ? void 0 : _b.includes('/api/v1/pixel')) {
package/package.json CHANGED
@@ -22,7 +22,7 @@
22
22
  "name": "Builder.io",
23
23
  "url": "https://www.builder.io"
24
24
  },
25
- "version": "0.11.4",
25
+ "version": "0.11.5",
26
26
  "homepage": "https://github.com/BuilderIO/mitosis",
27
27
  "main": "./dist/src/index.js",
28
28
  "exports": {