@base44-preview/vite-plugin 1.0.6-pr.63.d7abd73 → 1.0.18-pr.81.3d3652d

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 (52) hide show
  1. package/dist/html-injections-plugin.d.ts.map +1 -1
  2. package/dist/html-injections-plugin.js +4 -2
  3. package/dist/html-injections-plugin.js.map +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +17 -2
  6. package/dist/index.js.map +1 -1
  7. package/dist/injections/canvas-wheel-zoom-bridge.d.ts +5 -0
  8. package/dist/injections/canvas-wheel-zoom-bridge.d.ts.map +1 -0
  9. package/dist/injections/canvas-wheel-zoom-bridge.js +64 -0
  10. package/dist/injections/canvas-wheel-zoom-bridge.js.map +1 -0
  11. package/dist/injections/page-height-bridge.d.ts +14 -0
  12. package/dist/injections/page-height-bridge.d.ts.map +1 -0
  13. package/dist/injections/page-height-bridge.js +510 -0
  14. package/dist/injections/page-height-bridge.js.map +1 -0
  15. package/dist/injections/visual-edit-agent.d.ts.map +1 -1
  16. package/dist/injections/visual-edit-agent.js +16 -0
  17. package/dist/injections/visual-edit-agent.js.map +1 -1
  18. package/dist/jsx-processor.d.ts.map +1 -1
  19. package/dist/jsx-processor.js +1 -0
  20. package/dist/jsx-processor.js.map +1 -1
  21. package/dist/processors/collection-id-processor.d.ts +1 -0
  22. package/dist/processors/collection-id-processor.d.ts.map +1 -1
  23. package/dist/processors/collection-id-processor.js +17 -0
  24. package/dist/processors/collection-id-processor.js.map +1 -1
  25. package/dist/processors/collection-item-field-processor.d.ts.map +1 -1
  26. package/dist/processors/collection-item-field-processor.js +16 -5
  27. package/dist/processors/collection-item-field-processor.js.map +1 -1
  28. package/dist/processors/collection-item-id-processor.d.ts +27 -0
  29. package/dist/processors/collection-item-id-processor.d.ts.map +1 -1
  30. package/dist/processors/collection-item-id-processor.js +128 -1
  31. package/dist/processors/collection-item-id-processor.js.map +1 -1
  32. package/dist/processors/utils/collection-tracing-utils.d.ts +1 -1
  33. package/dist/processors/utils/collection-tracing-utils.d.ts.map +1 -1
  34. package/dist/processors/utils/collection-tracing-utils.js +48 -10
  35. package/dist/processors/utils/collection-tracing-utils.js.map +1 -1
  36. package/dist/processors/utils/shared-utils.js +1 -1
  37. package/dist/processors/utils/shared-utils.js.map +1 -1
  38. package/dist/statics/index.mjs +8 -8
  39. package/dist/statics/index.mjs.map +1 -1
  40. package/package.json +1 -1
  41. package/src/bridge.ts +1 -1
  42. package/src/html-injections-plugin.ts +4 -2
  43. package/src/index.ts +17 -2
  44. package/src/injections/canvas-wheel-zoom-bridge.ts +78 -0
  45. package/src/injections/page-height-bridge.ts +566 -0
  46. package/src/injections/visual-edit-agent.ts +26 -1
  47. package/src/jsx-processor.ts +1 -0
  48. package/src/processors/collection-id-processor.ts +17 -0
  49. package/src/processors/collection-item-field-processor.ts +23 -5
  50. package/src/processors/collection-item-id-processor.ts +154 -0
  51. package/src/processors/utils/collection-tracing-utils.ts +54 -8
  52. package/src/processors/utils/shared-utils.ts +1 -1
@@ -3,10 +3,15 @@ import { createLayerController } from "./layer-dropdown/controller.js";
3
3
  import { LAYER_DROPDOWN_ATTR } from "./layer-dropdown/consts.js";
4
4
  import { createInlineEditController } from "../capabilities/inline-edit/index.js";
5
5
  import { THEME_FONT_PREVIEW_ID } from "../consts.js";
6
+ import { createCanvasWheelZoomBridgeController } from "./canvas-wheel-zoom-bridge.js";
7
+ import { createPageHeightBridgeController } from "./page-height-bridge.js";
6
8
 
7
9
  const REPOSITION_DELAY_MS = 50;
8
10
 
9
11
  export function setupVisualEditAgent() {
12
+ const canvasWheelZoomBridge = createCanvasWheelZoomBridgeController();
13
+ const pageHeightBridge = createPageHeightBridgeController();
14
+
10
15
  // State variables (replacing React useState/useRef)
11
16
  let isVisualEditMode = false;
12
17
  let isPopoverDragging = false;
@@ -427,6 +432,7 @@ export function setupVisualEditAgent() {
427
432
  isVisualEditMode = isEnabled;
428
433
 
429
434
  if (!isEnabled) {
435
+ canvasWheelZoomBridge.disable();
430
436
  resumeAnimations();
431
437
  inlineEdit.stopEditing();
432
438
  clearSelection();
@@ -638,6 +644,25 @@ export function setupVisualEditAgent() {
638
644
  }
639
645
  break;
640
646
 
647
+ case "freeze-vh-units":
648
+ pageHeightBridge.freezeVhUnits(
649
+ typeof message.referenceVhBase === "number"
650
+ ? message.referenceVhBase
651
+ : undefined
652
+ );
653
+ break;
654
+
655
+ case "measure-page-height":
656
+ pageHeightBridge.measurePageHeight(
657
+ event.origin && event.origin !== "null" ? event.origin : "*",
658
+ typeof message.settleMs === "number" ? message.settleMs : undefined
659
+ );
660
+ break;
661
+
662
+ case "toggle-canvas-wheel-zoom-bridge":
663
+ canvasWheelZoomBridge.enable();
664
+ break;
665
+
641
666
  default:
642
667
  break;
643
668
  }
@@ -723,4 +748,4 @@ export function setupVisualEditAgent() {
723
748
 
724
749
  // Send ready message to parent
725
750
  window.parent.postMessage({ type: "visual-edit-agent-ready" }, "*");
726
- }
751
+ }
@@ -29,6 +29,7 @@ export class JSXProcessor {
29
29
 
30
30
  this.collectionIdProcessor.process(path);
31
31
  this.dataItemIdProcessor.process(path);
32
+ this.dataItemIdProcessor.processRootElement(path);
32
33
  this.dataItemFieldProcessor.process(path);
33
34
  this.staticArrayProcessor.process(path);
34
35
  }
@@ -251,6 +251,11 @@ export class CollectionIdProcessor {
251
251
 
252
252
  if (!this.callbackProducesJSX(callPath)) return;
253
253
 
254
+ // Skip map calls that are directly embedded as JSX children — those
255
+ // are already handled by tryDirectMapInChildren on their immediate
256
+ // parent, so attributing them to the component root would be wrong.
257
+ if (this.isCallInsideJSXChildren(callPath)) return;
258
+
254
259
  const arrayObj = callee.get("object") as NodePath<t.Expression>;
255
260
  info = this.tracingUtils.traceCollectionSource(arrayObj);
256
261
  },
@@ -258,4 +263,16 @@ export class CollectionIdProcessor {
258
263
 
259
264
  return info;
260
265
  }
266
+
267
+ private isCallInsideJSXChildren(path: NodePath): boolean {
268
+ let current: NodePath | null = path.parentPath;
269
+ while (current) {
270
+ if (current.isFunction()) break;
271
+ if (current.isJSXExpressionContainer()) {
272
+ return current.parentPath?.isJSXElement() ?? false;
273
+ }
274
+ current = current.parentPath;
275
+ }
276
+ return false;
277
+ }
261
278
  }
@@ -348,16 +348,34 @@ export class DataItemFieldProcessor {
348
348
  if (!param.isObjectPattern()) continue;
349
349
 
350
350
  for (const prop of param.get("properties")) {
351
+ if (!prop.isObjectProperty()) continue;
352
+ const key = (prop.node as t.ObjectProperty).key;
353
+ const val = (prop.node as t.ObjectProperty).value;
354
+
355
+ // Reuse a forwarding param already injected by processRootElement
356
+ if (
357
+ this.types.isStringLiteral(key) &&
358
+ key.value === DATA_COLLECTION_ITEM_ID &&
359
+ this.types.isIdentifier(val)
360
+ ) {
361
+ this.attributeUtils.addExpressionAttribute(
362
+ path,
363
+ DATA_COLLECTION_ITEM_ID,
364
+ this.types.identifier(val.name)
365
+ );
366
+ return;
367
+ }
368
+
369
+ // Use an existing id / _id destructured prop
351
370
  if (
352
- prop.isObjectProperty() &&
353
- this.types.isIdentifier(prop.node.key) &&
354
- (prop.node.key.name === "id" || prop.node.key.name === "_id") &&
355
- this.types.isIdentifier(prop.node.value)
371
+ this.types.isIdentifier(key) &&
372
+ (key.name === "id" || key.name === "_id") &&
373
+ this.types.isIdentifier(val)
356
374
  ) {
357
375
  this.attributeUtils.addExpressionAttribute(
358
376
  path,
359
377
  DATA_COLLECTION_ITEM_ID,
360
- this.types.identifier(prop.node.value.name)
378
+ this.types.identifier(val.name)
361
379
  );
362
380
  return;
363
381
  }
@@ -1,18 +1,24 @@
1
1
  import type { NodePath } from "@babel/traverse";
2
2
  import type * as t from "@babel/types";
3
3
  import { DATA_COLLECTION_ITEM_ID } from "../consts.js";
4
+ import { JSXUtils } from "../jsx-utils.js";
4
5
  import {
5
6
  JSXAttributeUtils,
6
7
  ExpressionAnalysisUtils,
8
+ PathNavigationUtils,
7
9
  } from "./utils/shared-utils.js";
8
10
 
11
+ const FORWARDING_PARAM_NAME = "__dataCollectionItemId";
12
+
9
13
  export class DataItemIdProcessor {
10
14
  private attributeUtils: JSXAttributeUtils;
11
15
  private expressionUtils: ExpressionAnalysisUtils;
16
+ private pathUtils: PathNavigationUtils;
12
17
 
13
18
  constructor(private types: typeof t) {
14
19
  this.attributeUtils = new JSXAttributeUtils(types);
15
20
  this.expressionUtils = new ExpressionAnalysisUtils(types);
21
+ this.pathUtils = new PathNavigationUtils(types);
16
22
  }
17
23
 
18
24
  process(path: NodePath<t.JSXOpeningElement>): void {
@@ -39,6 +45,154 @@ export class DataItemIdProcessor {
39
45
  );
40
46
  }
41
47
 
48
+ /**
49
+ * Called for every root DOM element of a component. Adds a `data-collection-item-id`
50
+ * forwarding attribute so that when the component is used as a collection item
51
+ * (with `data-collection-item-id` passed as a prop from a parent `.map()`), the
52
+ * attribute flows through to the actual DOM element.
53
+ *
54
+ * Skipped when:
55
+ * - The root element already spreads props (e.g. `{...props}`) — forwarding is automatic
56
+ * - The enclosing function maps over a collection — it's a container, not an item
57
+ */
58
+ processRootElement(path: NodePath<t.JSXOpeningElement>): void {
59
+ if (this.attributeUtils.hasAttribute(path, DATA_COLLECTION_ITEM_ID)) return;
60
+
61
+ // Only DOM or allowed custom elements (e.g. motion.div)
62
+ const name = JSXUtils.getElementName(path.node);
63
+ if (!name || !JSXUtils.isDOMOrAllowed(name)) return;
64
+
65
+ // Only the root return element of a component function
66
+ if (!this.pathUtils.isRootReturnElement(path)) return;
67
+
68
+ // Skip if root element already spreads all props — forwarding is handled naturally
69
+ if (this.hasSpreadAttribute(path)) return;
70
+
71
+ const fn = this.pathUtils.findEnclosingFunction(path);
72
+ if (!fn) return;
73
+
74
+ // Skip if this function IS a .map()/.flatMap() callback — it's an iteration
75
+ // function, not a component (e.g. [...Array(3)].map((_, i) => <div />))
76
+ if (this.isMapCallbackFunction(fn)) return;
77
+
78
+ // Skip if this function maps over a collection — it's a container, not an item
79
+ if (this.functionContainsMapCall(fn)) return;
80
+
81
+ const expr = this.buildCollectionItemIdExpression(fn);
82
+ if (!expr) return;
83
+
84
+ this.attributeUtils.addExpressionAttribute(path, DATA_COLLECTION_ITEM_ID, expr);
85
+ }
86
+
87
+ private hasSpreadAttribute(path: NodePath<t.JSXOpeningElement>): boolean {
88
+ return path.node.attributes.some((attr) =>
89
+ this.types.isJSXSpreadAttribute(attr)
90
+ );
91
+ }
92
+
93
+ /**
94
+ * Returns true when `fn` is a direct callback argument to a `.map()` or
95
+ * `.flatMap()` call — i.e. the function is an iteration callback, not a
96
+ * component definition. This prevents injecting prop-forwarding into
97
+ * patterns like `[...Array(3)].map((_, i) => <div />)` where the first
98
+ * param may be `undefined`.
99
+ */
100
+ private isMapCallbackFunction(fn: NodePath<t.Function>): boolean {
101
+ const parent = fn.parentPath;
102
+ if (!parent?.isCallExpression()) return false;
103
+ const callee = parent.node.callee;
104
+ return (
105
+ this.types.isMemberExpression(callee) &&
106
+ this.types.isIdentifier(callee.property) &&
107
+ (callee.property.name === "map" || callee.property.name === "flatMap")
108
+ );
109
+ }
110
+
111
+ private functionContainsMapCall(fn: NodePath<t.Function>): boolean {
112
+ let found = false;
113
+ const types = this.types;
114
+ fn.traverse({
115
+ // Stop at nested functions — event handlers / callbacks defined inside the
116
+ // component should not cause it to be treated as a collection container.
117
+ Function(innerFnPath) {
118
+ innerFnPath.skip();
119
+ },
120
+ CallExpression(callPath) {
121
+ if (found) {
122
+ callPath.stop();
123
+ return;
124
+ }
125
+ const callee = callPath.node.callee;
126
+ if (
127
+ types.isMemberExpression(callee) &&
128
+ types.isIdentifier(callee.property) &&
129
+ (callee.property.name === "map" || callee.property.name === "flatMap")
130
+ ) {
131
+ found = true;
132
+ }
133
+ },
134
+ });
135
+ return found;
136
+ }
137
+
138
+ /**
139
+ * Returns an expression for reading `data-collection-item-id` from the component's
140
+ * props, modifying the function's params destructuring if necessary.
141
+ */
142
+ private buildCollectionItemIdExpression(
143
+ fn: NodePath<t.Function>
144
+ ): t.Expression | null {
145
+ const params = fn.get("params");
146
+ const firstParam = Array.isArray(params) ? params[0] : params;
147
+ if (!firstParam) return null;
148
+
149
+ if (firstParam.isObjectPattern()) {
150
+ // Check if already destructured (key must be a string literal — hyphens in the
151
+ // attribute name rule out a plain identifier)
152
+ for (const prop of firstParam.get("properties")) {
153
+ if (!prop.isObjectProperty()) continue;
154
+ const key = (prop.node as t.ObjectProperty).key;
155
+ const val = (prop.node as t.ObjectProperty).value;
156
+ if (
157
+ this.types.isStringLiteral(key) &&
158
+ key.value === DATA_COLLECTION_ITEM_ID &&
159
+ this.types.isIdentifier(val)
160
+ ) {
161
+ return this.types.identifier(val.name);
162
+ }
163
+ }
164
+
165
+ // Add "data-collection-item-id": __dataCollectionItemId to destructuring
166
+ const newProp = this.types.objectProperty(
167
+ this.types.stringLiteral(DATA_COLLECTION_ITEM_ID),
168
+ this.types.identifier(FORWARDING_PARAM_NAME)
169
+ );
170
+
171
+ const restIdx = firstParam.node.properties.findIndex((p) =>
172
+ this.types.isRestElement(p)
173
+ );
174
+ if (restIdx === -1) {
175
+ firstParam.node.properties.push(newProp);
176
+ } else {
177
+ firstParam.node.properties.splice(restIdx, 0, newProp);
178
+ }
179
+
180
+ return this.types.identifier(FORWARDING_PARAM_NAME);
181
+ }
182
+
183
+ if (firstParam.isIdentifier()) {
184
+ // props?.["data-collection-item-id"] — optional access in case param is nullish
185
+ return this.types.optionalMemberExpression(
186
+ this.types.identifier(firstParam.node.name),
187
+ this.types.stringLiteral(DATA_COLLECTION_ITEM_ID),
188
+ true, // computed
189
+ true // optional
190
+ );
191
+ }
192
+
193
+ return null;
194
+ }
195
+
42
196
  private findKeyAttribute(
43
197
  path: NodePath<t.JSXOpeningElement>
44
198
  ): t.JSXAttribute | null {
@@ -2,20 +2,17 @@ import type { NodePath } from "@babel/traverse";
2
2
  import type * as t from "@babel/types";
3
3
  import type { CollectionInfo } from "../../capabilities/inline-edit/types.js";
4
4
  import {
5
- ExpressionAnalysisUtils,
6
5
  BindingUtils,
7
6
  CallExpressionUtils,
8
7
  } from "./shared-utils.js";
9
8
 
10
9
  export class CollectionTracingUtils {
11
- private expressionUtils: ExpressionAnalysisUtils;
12
10
  private bindingUtils: BindingUtils;
13
11
  private callUtils: CallExpressionUtils;
14
12
  private visitedSetters = new Set<string>();
15
13
  private visitedGetByIdSetters = new Set<string>();
16
14
 
17
15
  constructor(private types: typeof t) {
18
- this.expressionUtils = new ExpressionAnalysisUtils(types);
19
16
  this.bindingUtils = new BindingUtils(types);
20
17
  this.callUtils = new CallExpressionUtils(types);
21
18
  }
@@ -104,6 +101,14 @@ export class CollectionTracingUtils {
104
101
  return this.traceExpression(init);
105
102
  }
106
103
 
104
+ if (init.isConditionalExpression()) {
105
+ const consequent = init.get("consequent") as NodePath<t.Expression>;
106
+ const alternate = init.get("alternate") as NodePath<t.Expression>;
107
+ const result = this.traceExpression(consequent);
108
+ if (result) return result;
109
+ return this.traceExpression(alternate);
110
+ }
111
+
107
112
  if (init.isMemberExpression()) {
108
113
  return this.traceExpression(init);
109
114
  }
@@ -137,18 +142,59 @@ export class CollectionTracingUtils {
137
142
  this.visitedSetters.add(setterName);
138
143
 
139
144
  const functionScope = declaratorPath.getFunctionParent() ?? declaratorPath.scope.path;
145
+
146
+ // Pattern 1: setData(result) — direct call
140
147
  const setterCall = this.bindingUtils.findSetterCallInScope(
141
148
  setterName,
142
149
  functionScope
143
150
  );
151
+ if (setterCall) {
152
+ const args = setterCall.get("arguments");
153
+ const firstArg = args[0];
154
+ if (!firstArg || !firstArg.isExpression()) return null;
155
+ return this.traceExpression(firstArg);
156
+ }
144
157
 
145
- if (!setterCall) return null;
158
+ // Pattern 2: somePromise.then(setData) setter passed as .then callback
159
+ const thenCall = this.findThenCallWithSetter(setterName, functionScope);
160
+ if (thenCall) {
161
+ const callee = thenCall.get("callee");
162
+ if (callee.isMemberExpression()) {
163
+ const promiseExpr = callee.get("object") as NodePath<t.Expression>;
164
+ return this.traceExpression(promiseExpr);
165
+ }
166
+ }
146
167
 
147
- const args = setterCall.get("arguments");
148
- const firstArg = args[0];
149
- if (!firstArg || !firstArg.isExpression()) return null;
168
+ return null;
169
+ }
150
170
 
151
- return this.traceExpression(firstArg);
171
+ private findThenCallWithSetter(
172
+ setterName: string,
173
+ scope: NodePath
174
+ ): NodePath<t.CallExpression> | null {
175
+ let result: NodePath<t.CallExpression> | null = null;
176
+
177
+ scope.traverse({
178
+ CallExpression: (callPath: NodePath<t.CallExpression>) => {
179
+ if (result) {
180
+ callPath.stop();
181
+ return;
182
+ }
183
+ const callee = callPath.node.callee;
184
+ if (
185
+ this.types.isMemberExpression(callee) &&
186
+ this.types.isIdentifier(callee.property) &&
187
+ callee.property.name === "then" &&
188
+ callPath.node.arguments.length > 0 &&
189
+ this.types.isIdentifier(callPath.node.arguments[0]) &&
190
+ callPath.node.arguments[0].name === setterName
191
+ ) {
192
+ result = callPath;
193
+ }
194
+ },
195
+ });
196
+
197
+ return result;
152
198
  }
153
199
 
154
200
  private traceMemberExpression(
@@ -541,7 +541,7 @@ export class CallExpressionUtils {
541
541
  const method = callee.property;
542
542
  if (
543
543
  !this.types.isIdentifier(method) ||
544
- (method.name !== "list" && method.name !== "getAll")
544
+ (method.name !== "list" && method.name !== "getAll" && method.name !== "filter")
545
545
  ) {
546
546
  return null;
547
547
  }