@wavemaker/react-codegen 11.14.2-1.245 → 12.0.0-next.28533

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 (100) hide show
  1. package/build-react-web.js +0 -19
  2. package/build-react-web.js.map +1 -1
  3. package/package-lock.json +136 -132
  4. package/package.json +2 -2
  5. package/src/app.generator.js +176 -18
  6. package/src/app.generator.js.map +1 -1
  7. package/src/gen-app-override-css.js +1 -1
  8. package/src/gen-app-override-css.js.map +1 -1
  9. package/src/handlebar-helpers.js +116 -19
  10. package/src/handlebar-helpers.js.map +1 -1
  11. package/src/project.service.js +47 -5
  12. package/src/project.service.js.map +1 -1
  13. package/src/transpile/bind.ex.transformer.js +11 -1
  14. package/src/transpile/bind.ex.transformer.js.map +1 -1
  15. package/src/transpile/components/basic/richtexteditor.transformer.js +3 -2
  16. package/src/transpile/components/basic/richtexteditor.transformer.js.map +1 -1
  17. package/src/transpile/components/basic/search.transformer.js +4 -6
  18. package/src/transpile/components/basic/search.transformer.js.map +1 -1
  19. package/src/transpile/components/container/tabpane.transformer.js +1 -1
  20. package/src/transpile/components/container/tabpane.transformer.js.map +1 -1
  21. package/src/transpile/components/container/tabs.transformer.js +4 -0
  22. package/src/transpile/components/container/tabs.transformer.js.map +1 -1
  23. package/src/transpile/components/data/card/card-content.transformer.js +5 -1
  24. package/src/transpile/components/data/card/card-content.transformer.js.map +1 -1
  25. package/src/transpile/components/data/form/form-action.transformer.js +73 -18
  26. package/src/transpile/components/data/form/form-action.transformer.js.map +1 -1
  27. package/src/transpile/components/data/form/form-field.transformer.js +46 -11
  28. package/src/transpile/components/data/form/form-field.transformer.js.map +1 -1
  29. package/src/transpile/components/data/form/form.transformer.js +53 -17
  30. package/src/transpile/components/data/form/form.transformer.js.map +1 -1
  31. package/src/transpile/components/data/list/list-transformer.js +64 -33
  32. package/src/transpile/components/data/list/list-transformer.js.map +1 -1
  33. package/src/transpile/components/data/live-filter-field.transformer.js +65 -10
  34. package/src/transpile/components/data/live-filter-field.transformer.js.map +1 -1
  35. package/src/transpile/components/data/liveform.transformer.js +11 -4
  36. package/src/transpile/components/data/liveform.transformer.js.map +1 -1
  37. package/src/transpile/components/data/table/table-action.transformer.js +6 -2
  38. package/src/transpile/components/data/table/table-action.transformer.js.map +1 -1
  39. package/src/transpile/components/data/table/table-column.transformer.js +95 -20
  40. package/src/transpile/components/data/table/table-column.transformer.js.map +1 -1
  41. package/src/transpile/components/data/table/table-group.transformer.js +16 -0
  42. package/src/transpile/components/data/table/table-group.transformer.js.map +1 -0
  43. package/src/transpile/components/data/table/table-row-action.transformer.js +6 -2
  44. package/src/transpile/components/data/table/table-row-action.transformer.js.map +1 -1
  45. package/src/transpile/components/data/table/table-row.transformer.js +7 -4
  46. package/src/transpile/components/data/table/table-row.transformer.js.map +1 -1
  47. package/src/transpile/components/data/table/table.transformer.js +29 -1
  48. package/src/transpile/components/data/table/table.transformer.js.map +1 -1
  49. package/src/transpile/components/data/table/utils.js +3 -1
  50. package/src/transpile/components/data/table/utils.js.map +1 -1
  51. package/src/transpile/components/form/button.transformer.js +12 -1
  52. package/src/transpile/components/form/button.transformer.js.map +1 -1
  53. package/src/transpile/components/input/checkbox-set.transformer.js +1 -1
  54. package/src/transpile/components/input/checkbox-set.transformer.js.map +1 -1
  55. package/src/transpile/components/input/chips.transformer.js +2 -1
  56. package/src/transpile/components/input/chips.transformer.js.map +1 -1
  57. package/src/transpile/components/input/color-picker.transformer.js +2 -1
  58. package/src/transpile/components/input/color-picker.transformer.js.map +1 -1
  59. package/src/transpile/components/input/rating.transformer.js +1 -1
  60. package/src/transpile/components/input/rating.transformer.js.map +1 -1
  61. package/src/transpile/components/input/select.transformer.js +1 -1
  62. package/src/transpile/components/input/select.transformer.js.map +1 -1
  63. package/src/transpile/components/input/text.transformer.js +3 -1
  64. package/src/transpile/components/input/text.transformer.js.map +1 -1
  65. package/src/transpile/components/input/upload.transformer.js +19 -0
  66. package/src/transpile/components/input/upload.transformer.js.map +1 -0
  67. package/src/transpile/components/nav/navbar.transformer.js +84 -0
  68. package/src/transpile/components/nav/navbar.transformer.js.map +1 -1
  69. package/src/transpile/components/navigation/popover.transformer.js +1 -1
  70. package/src/transpile/components/navigation/popover.transformer.js.map +1 -1
  71. package/src/transpile/components/page/partial-container.transformer.js +15 -6
  72. package/src/transpile/components/page/partial-container.transformer.js.map +1 -1
  73. package/src/transpile/components/prefab/prefab.transformer.js +23 -2
  74. package/src/transpile/components/prefab/prefab.transformer.js.map +1 -1
  75. package/src/transpile/components/transform-register.js +4 -0
  76. package/src/transpile/components/transform-register.js.map +1 -1
  77. package/src/transpile/components/utils.js +176 -15
  78. package/src/transpile/components/utils.js.map +1 -1
  79. package/src/transpile/property/property-parser.js +12 -0
  80. package/src/transpile/property/property-parser.js.map +1 -1
  81. package/src/transpile/style.transformer.js +7 -1
  82. package/src/transpile/style.transformer.js.map +1 -1
  83. package/src/transpile/transpile.js +370 -25
  84. package/src/transpile/transpile.js.map +1 -1
  85. package/src/utils.js +343 -36
  86. package/src/utils.js.map +1 -1
  87. package/src/utils.test.js +703 -0
  88. package/src/utils.test.js.map +1 -0
  89. package/src/variables/variable.transformer.js +302 -135
  90. package/src/variables/variable.transformer.js.map +1 -1
  91. package/templates/component/app.template.hbs +1 -0
  92. package/templates/component/component.hbs +12 -1
  93. package/templates/component/partial.hbs +8 -0
  94. package/templates/project/.env +1 -1
  95. package/templates/project/app/components.css +16 -15
  96. package/templates/project/app/globals.css +39 -0
  97. package/templates/project/app/loading.tsx +1 -1
  98. package/templates/project/package.json +8 -8
  99. package/templates/variables.template +21 -2
  100. package/templates/project/.npmrc +0 -5
@@ -47,6 +47,27 @@ const utils_1 = require("../utils");
47
47
  const id_generator_1 = require("./id-generator");
48
48
  const generateRandomString = (length = 10) => Math.random().toString(20).substr(2, length);
49
49
  exports.generateRandomString = generateRandomString;
50
+ /**
51
+ * Processes text content for JSX compatibility
52
+ * If content is only special characters (like > or <), wraps them in React expressions
53
+ * @param textContent The raw text content from the node
54
+ * @returns Processed text content safe for JSX, or empty string if no valid content
55
+ */
56
+ const processTextContentForJSX = (textContent) => {
57
+ const trimmedContent = textContent.trim();
58
+ if (!trimmedContent) {
59
+ return "";
60
+ }
61
+ // If content is only special characters (like > or <), wrap in React expression
62
+ if (/^[<>&]+$/.test(trimmedContent)) {
63
+ // Convert each special character to React expression
64
+ return trimmedContent
65
+ .split("")
66
+ .map(char => `{'${char}'}`)
67
+ .join("");
68
+ }
69
+ return textContent;
70
+ };
50
71
  // Input widgets that should inherit required/floating-label behavior from wm-composite
51
72
  const COMPOSITE_INPUT_TAGS = new Set([
52
73
  "wm-text",
@@ -81,7 +102,7 @@ const addFragmentOptionalChaining = (expression, addFallback = false) => {
81
102
  detectFunctionCalls: true,
82
103
  ...(addFallback && { fallbackValue: '""' }),
83
104
  };
84
- return (0, utils_1.addOptionalChaining)(expression, chainingObj);
105
+ return (0, utils_1.addOptionalChaining)(expression);
85
106
  };
86
107
  class TranspilationContext {
87
108
  constructor(args) {
@@ -105,6 +126,7 @@ class Transpiler {
105
126
  this.transformers = new Map();
106
127
  this.componentNamesList = [];
107
128
  this.transpile = (e, isPartOfPrefab, splitCode = true, oldContext) => {
129
+ var _a;
108
130
  const isHtmlElement = utils_1.htmlElements.includes(e.tagName.toLowerCase());
109
131
  const ignoreElements = ["wm-filter-action", "wm-form-action"];
110
132
  const transformer = this.transformers.get(e && e.tagName.toLowerCase());
@@ -116,7 +138,10 @@ class Transpiler {
116
138
  partials: [],
117
139
  prefabs: [],
118
140
  };
119
- if (!e.getAttribute("name") && !ignoreElements.includes(e.tagName.toLowerCase())) {
141
+ // Skip name auto-generation for elements inside table column or explicitly ignored elements
142
+ if (!e.getAttribute("name") &&
143
+ !ignoreElements.includes(e.tagName.toLowerCase()) &&
144
+ !((_a = oldContext === null || oldContext === void 0 ? void 0 : oldContext.data) === null || _a === void 0 ? void 0 : _a.isInsideTableColumn)) {
120
145
  e.setAttribute("name", `${e.tagName.toLowerCase().replace(/-/g, "_")}_${(0, exports.generateRandomString)()}`);
121
146
  }
122
147
  // for every new page, initially context is empty. Hence clearing the prev page componentNamesList array.
@@ -137,9 +162,9 @@ class Transpiler {
137
162
  }
138
163
  else if (c && c.nodeType == node_html_parser_1.NodeType.TEXT_NODE) {
139
164
  // Preserve text nodes
140
- const textContent = c.text;
141
- if (textContent.trim()) {
142
- result.markup += textContent;
165
+ const processedContent = processTextContentForJSX(c.text);
166
+ if (processedContent) {
167
+ result.markup += processedContent;
143
168
  }
144
169
  }
145
170
  });
@@ -170,9 +195,9 @@ class Transpiler {
170
195
  }
171
196
  else if (c && c.nodeType == node_html_parser_1.NodeType.TEXT_NODE) {
172
197
  // Handle text nodes for custom elements
173
- const textContent = c.text;
174
- if (textContent.trim()) {
175
- result.markup += textContent;
198
+ const processedContent = processTextContentForJSX(c.text);
199
+ if (processedContent) {
200
+ result.markup += processedContent;
176
201
  }
177
202
  }
178
203
  });
@@ -189,18 +214,144 @@ class Transpiler {
189
214
  return result;
190
215
  };
191
216
  }
217
+ /**
218
+ * Transforms $[...] syntax to fragment.property for use with Angular parser
219
+ * Examples:
220
+ * $[location] -> fragment.location
221
+ * $[data[$i].locationDesc] -> fragment.locationDesc (removes data[$i]. prefix)
222
+ */
223
+ transformDollarBracketForFragmentContext(expression) {
224
+ // First, remove data[$i]. prefixes from within $[...] brackets
225
+ expression = expression.replace(/\$\[data\[\$i\]\./g, "$[");
226
+ // Transform $[property.path] to fragment.property.path
227
+ expression = expression.replace(/\$\[([^\]]+)\]/g, (match, property) => {
228
+ // Remove any remaining data[index]. patterns
229
+ property = property.replace(/data\[\d+\]\./g, "");
230
+ return `${property}`;
231
+ });
232
+ return expression;
233
+ }
234
+ /**
235
+ * Checks if a parent element has a dataset that matches the base path of the current dataset.
236
+ * This is used to determine if [$i] should be handled by parent's $item transformation.
237
+ * Example: child has "Variables.list.dataSet[$i].Features", parent has "Variables.list.dataSet"
238
+ */
239
+ hasParentWithMatchingDataset(element, basePath) {
240
+ var _a;
241
+ let parent = element.parentNode;
242
+ while (parent) {
243
+ const parentDataset = (_a = parent.getAttribute) === null || _a === void 0 ? void 0 : _a.call(parent, "dataset");
244
+ if (parentDataset) {
245
+ // Remove bind: prefix if present
246
+ const parentDatasetValue = parentDataset.startsWith("bind:")
247
+ ? parentDataset.substring(5)
248
+ : parentDataset;
249
+ // Check if parent dataset matches the base path (the array being iterated)
250
+ if (parentDatasetValue === basePath || parentDatasetValue === `fragment.${basePath}`) {
251
+ return true;
252
+ }
253
+ }
254
+ parent = parent.parentNode;
255
+ }
256
+ return false;
257
+ }
258
+ /**
259
+ * Transforms dataset expressions containing [$i] into a map function.
260
+ * Handles multiple [$i] occurrences by creating nested map chains.
261
+ */
262
+ transformDatasetWithIndex(value) {
263
+ // Check if there are any [$i] patterns
264
+ if (!value.includes("[$i]")) {
265
+ // Fallback to normal transformation if pattern doesn't match
266
+ return "bind:" + (0, bind_ex_transformer_1.default)(value, "fragment", "attr");
267
+ }
268
+ // Split the path by [$i] to get all segments
269
+ const segments = value.split("[$i]");
270
+ if (segments.length < 2) {
271
+ // Should not happen if includes("[$i]") is true, but safety check
272
+ return "bind:" + (0, bind_ex_transformer_1.default)(value, "fragment", "attr");
273
+ }
274
+ // First segment is the base path
275
+ const basePath = segments[0];
276
+ const transformedBase = (0, utils_1.addOptionalChaining)(`fragment.${basePath}`);
277
+ /**
278
+ * Recursively builds nested map expressions for each [$i] occurrence
279
+ * @param segmentIndex - Current segment index (starting from 1)
280
+ * @param itemVar - Variable name for the current iteration item
281
+ * @returns The nested map expression
282
+ */
283
+ const buildNestedMap = (segmentIndex, itemVar) => {
284
+ const segment = segments[segmentIndex];
285
+ // Remove leading dot if present and trim whitespace
286
+ let cleanSegment = segment.startsWith(".") ? segment.substring(1) : segment;
287
+ cleanSegment = cleanSegment.trim();
288
+ const isLastSegment = segmentIndex === segments.length - 1;
289
+ if (isLastSegment) {
290
+ // Last segment: extract the final property
291
+ if (cleanSegment) {
292
+ return (0, utils_1.addOptionalChaining)(`${itemVar}.${cleanSegment}`);
293
+ }
294
+ else {
295
+ return itemVar;
296
+ }
297
+ }
298
+ else {
299
+ // Intermediate segment: need to handle next [$i]
300
+ const nextItemVar = segmentIndex === 1 ? "child" : `item${segmentIndex}`;
301
+ const nextExpression = buildNestedMap(segmentIndex + 1, nextItemVar);
302
+ if (cleanSegment) {
303
+ // Build nested map: currentItem.property.map(nextItem => ...)
304
+ const propertyAccess = (0, utils_1.addOptionalChaining)(`${itemVar}.${cleanSegment}`);
305
+ return `${propertyAccess}?.map(${nextItemVar} => ${nextExpression})`;
306
+ }
307
+ else {
308
+ // No property, just map over the item itself
309
+ return `${itemVar}?.map(${nextItemVar} => ${nextExpression})`;
310
+ }
311
+ }
312
+ };
313
+ // Build the complete expression with nested maps
314
+ const mapExpression = `${transformedBase}?.map(item => ${buildNestedMap(1, "item")}) || []`;
315
+ return `mapfn:() => ${mapExpression}`;
316
+ }
192
317
  preTranspile(e) {
193
318
  Object.keys(e.attributes).forEach(name => {
194
319
  let value = e.attributes[name];
195
320
  if (value.startsWith("bind:")) {
196
- value = value.substring(5);
321
+ value = value.substring(5).trim();
322
+ // Remove trailing semicolons (common in WaveMaker expressions but invalid in JSX)
323
+ value = value.replace(/;+$/, "");
197
324
  // Check if this is a simple single $[...] expression that can be simplified
198
325
  const simpleSinglePattern = /^\$\[([^\]]+)\]$/;
199
326
  const match = value.match(simpleSinglePattern);
200
327
  if (match) {
201
- e.setAttribute(name, match[1]);
328
+ // Extract property path and remove data[$i]. prefix if present
329
+ let propertyPath = match[1];
330
+ propertyPath = propertyPath.replace(/^data\[\$i\]\./, "");
331
+ propertyPath = propertyPath.replace(/^data\[\d+\]\./, "");
332
+ e.setAttribute(name, propertyPath);
333
+ }
334
+ else if (name === "dataset" && value.includes("[$i]")) {
335
+ // Check if this [$i] should be handled by a parent's $item transformation
336
+ // Extract base path (everything before [$i])
337
+ const basePathMatch = value.match(/^(.+?)\[\$i\]/);
338
+ const basePath = basePathMatch ? basePathMatch[1] : null;
339
+ if (basePath && this.hasParentWithMatchingDataset(e, basePath)) {
340
+ // Parent has matching dataset - let transformRepeatChildData handle [$i] → $item
341
+ // Transform $[...] syntax before passing to Angular parser
342
+ value = this.transformDollarBracketForFragmentContext(value);
343
+ let transformedValue = (0, bind_ex_transformer_1.default)(value, "fragment", "attr");
344
+ e.setAttribute(name, "bind:" + transformedValue);
345
+ }
346
+ else {
347
+ // No parent with matching dataset - generate map function
348
+ const datasetMapResult = this.transformDatasetWithIndex(value);
349
+ e.setAttribute(name, datasetMapResult);
350
+ }
202
351
  }
203
352
  else {
353
+ // Transform $[...] syntax before passing to Angular parser
354
+ value = this.transformDollarBracketForFragmentContext(value);
204
355
  let transformedValue = (0, bind_ex_transformer_1.default)(value, "fragment", "attr");
205
356
  // Apply optional chaining with fallback only for backgroundimage
206
357
  if (name === "backgroundimage") {
@@ -313,7 +464,7 @@ class Transpiler {
313
464
  if (!condition) {
314
465
  return `'${className.trim()}'`;
315
466
  }
316
- return `(${condition.trim()} ? ${className.replace(/'$/, " '")} : '')`;
467
+ return `(${(0, utils_1.addOptionalChaining)(condition.trim())} ? ${className.replace(/'$/, " '")} : '')`;
317
468
  });
318
469
  conditionalClasses = expressions.join("+");
319
470
  }
@@ -346,7 +497,7 @@ class Transpiler {
346
497
  className: classes,
347
498
  };
348
499
  }
349
- findArgs(str) {
500
+ findArgs(str, context) {
350
501
  var _a;
351
502
  let argStr = "";
352
503
  let maxSplits = 0;
@@ -358,13 +509,46 @@ class Transpiler {
358
509
  }
359
510
  });
360
511
  argStr = argStr.replace(/\(|\)/g, "");
361
- if (argStr.indexOf("currentItemWidgets") > 0) {
512
+ if (argStr.indexOf("currentItemWidgets") > 0 && (context === null || context === void 0 ? void 0 : context.get("isInsideList"))) {
362
513
  argStr = argStr
363
514
  .split(",")
364
515
  // implement the logic for currentItemWidgets in proxy mechanism for looped items
365
516
  .filter(s => !(0, lodash_1.includes)(["$item", "currentItemWidgets"], s.trim()))
366
517
  .join(",");
367
518
  }
519
+ // Parse arguments properly, handling commas inside object literals and arrays
520
+ const parseArguments = (argString) => {
521
+ const args = [];
522
+ let currentArg = "";
523
+ let braceCount = 0;
524
+ let bracketCount = 0;
525
+ for (let i = 0; i < argString.length; i++) {
526
+ const char = argString[i];
527
+ // Track object literals (curly braces) and arrays (square brackets)
528
+ if (char === "{")
529
+ braceCount++;
530
+ else if (char === "}")
531
+ braceCount--;
532
+ else if (char === "[")
533
+ bracketCount++;
534
+ else if (char === "]")
535
+ bracketCount--;
536
+ // If we encounter a comma at the top level (not inside an object or array), it's an argument separator
537
+ if (char === "," && braceCount === 0 && bracketCount === 0) {
538
+ if (currentArg.trim()) {
539
+ args.push(currentArg.trim());
540
+ }
541
+ currentArg = "";
542
+ continue;
543
+ }
544
+ currentArg += char;
545
+ }
546
+ // Add the last argument
547
+ if (currentArg.trim()) {
548
+ args.push(currentArg.trim());
549
+ }
550
+ return args;
551
+ };
368
552
  // Droping literal arguments like strings, numbers, booleans, null/undefined
369
553
  const isLiteral = (s) => {
370
554
  const v = s.trim();
@@ -376,12 +560,77 @@ class Transpiler {
376
560
  const isPropertyAccess = (s) => {
377
561
  return s.trim().includes(".");
378
562
  };
379
- const args = argStr
380
- .split(",")
381
- .map(s => s.trim())
563
+ // Object literals should not be extracted as function parameters
564
+ const isObjectLiteral = (s) => {
565
+ const trimmed = s.trim();
566
+ // Check if it starts with { and contains at least one : (key-value pair)
567
+ if (trimmed.startsWith("{") && trimmed.includes(":")) {
568
+ // Verify it's a balanced object literal
569
+ let braceCount = 0;
570
+ let inString = false;
571
+ let stringChar = "";
572
+ for (let i = 0; i < trimmed.length; i++) {
573
+ const char = trimmed[i];
574
+ if ((char === '"' || char === "'") && (i === 0 || trimmed[i - 1] !== "\\")) {
575
+ if (!inString) {
576
+ inString = true;
577
+ stringChar = char;
578
+ }
579
+ else if (char === stringChar) {
580
+ inString = false;
581
+ }
582
+ }
583
+ else if (!inString) {
584
+ if (char === "{")
585
+ braceCount++;
586
+ else if (char === "}")
587
+ braceCount--;
588
+ }
589
+ }
590
+ // If braces are balanced and it ends with }, it's an object literal
591
+ return braceCount === 0 && trimmed.endsWith("}");
592
+ }
593
+ return false;
594
+ };
595
+ // Array literals should not be extracted as function parameters
596
+ const isArrayLiteral = (s) => {
597
+ const trimmed = s.trim();
598
+ // Check if it starts with [ and ends with ]
599
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
600
+ // Verify it's a balanced array literal
601
+ let bracketCount = 0;
602
+ let inString = false;
603
+ let stringChar = "";
604
+ for (let i = 0; i < trimmed.length; i++) {
605
+ const char = trimmed[i];
606
+ if ((char === '"' || char === "'") && (i === 0 || trimmed[i - 1] !== "\\")) {
607
+ if (!inString) {
608
+ inString = true;
609
+ stringChar = char;
610
+ }
611
+ else if (char === stringChar) {
612
+ inString = false;
613
+ }
614
+ }
615
+ else if (!inString) {
616
+ if (char === "[")
617
+ bracketCount++;
618
+ else if (char === "]")
619
+ bracketCount--;
620
+ }
621
+ }
622
+ // If brackets are balanced, it's an array literal
623
+ return bracketCount === 0;
624
+ }
625
+ return false;
626
+ };
627
+ const parsedArgs = parseArguments(argStr);
628
+ const args = parsedArgs
382
629
  .filter(Boolean)
383
630
  .filter(s => !isLiteral(s))
384
- .filter(s => !isPropertyAccess(s));
631
+ .filter(s => !isPropertyAccess(s))
632
+ .filter(s => !isObjectLiteral(s))
633
+ .filter(s => !isArrayLiteral(s));
385
634
  return `(${args.join(", ")})`;
386
635
  }
387
636
  quoteAttr(v) {
@@ -392,12 +641,20 @@ class Transpiler {
392
641
  .replace(/</g, "&lt;")
393
642
  .replace(/>/g, "&gt;");
394
643
  }
644
+ /**
645
+ * Converts ternary expressions that return string literals "true"/"false" to actual boolean values
646
+ * Example: (condition?"true":"false") -> (condition?true:false)
647
+ * Handles cases like: (condition?"true":"false"), (condition? "true" : "false"), etc.
648
+ */
649
+ convertStringBooleanTernary(exp) {
650
+ return exp.replace(/\?\s*["'](true|false)["']\s*:\s*["'](true|false)["']/g, (_, g1, g2) => `?${g1}:${g2}`);
651
+ }
395
652
  transformAttr(tagName, name, value, context, tx) {
396
653
  if (name === "styles" || name === "style" || name === "className") {
397
654
  // continue
398
655
  }
399
656
  else if (name === "groupby" && value.includes("(")) {
400
- value = `${this.findArgs(value)} => ${value}`;
657
+ value = `${this.findArgs(value, context)} => ${value}`;
401
658
  value = `{${value}}`;
402
659
  }
403
660
  else if (name.startsWith("on-")) {
@@ -405,7 +662,11 @@ class Transpiler {
405
662
  value = value.replace(/item,/g, "$item,");
406
663
  value = value.replace(/item\./g, "$item.");
407
664
  value = value.replace(/item\)/g, "$item)");
408
- value = `${this.findArgs(value)} => {${value}}`;
665
+ const args = this.findArgs(value, context);
666
+ if (context === null || context === void 0 ? void 0 : context.get("isInsideTableColumn")) {
667
+ value = value.replace(/\(row\)/g, "(rowData)");
668
+ }
669
+ value = `${args} => {${value}}`;
409
670
  value = `{${value}}`;
410
671
  }
411
672
  else if (name === "datasource" || name === "deletedatasource") {
@@ -413,10 +674,27 @@ class Transpiler {
413
674
  value = `{${value}}`;
414
675
  }
415
676
  }
677
+ else if (value.startsWith("mapfn:")) {
678
+ // Handle dataset with $i - wrapped as a function
679
+ const exp = value.substring(6); // Remove "mapfn:" prefix
680
+ value = `{${exp}}`;
681
+ }
416
682
  else if (value.startsWith("bind:")) {
417
683
  let exp = `${value.substring(5)}`;
684
+ exp = exp.replace("fragment.Page", "fragment");
685
+ const isProgressCircleDatavalue = tagName === "wm-progress-circle" && name === "datavalue";
418
686
  exp = (0, utils_1.modifyExpression)(exp);
419
- value = name === "show" ? `{Boolean(${exp})}` : `{${exp}}`;
687
+ exp = this.convertStringBooleanTernary(exp);
688
+ // If expression is empty after transformation, use empty string instead of empty braces
689
+ if (!exp) {
690
+ exp = '""';
691
+ }
692
+ if (isProgressCircleDatavalue) {
693
+ value = `{(${exp} || 0)}`; // for progress circle datavalue, we need to set the value to 0 if it is null or undefined
694
+ }
695
+ else {
696
+ value = name === "show" ? `{(${exp} ?? false)}` : `{${exp}}`;
697
+ }
420
698
  if (name === "conditionalclass") {
421
699
  return "";
422
700
  }
@@ -503,7 +781,7 @@ class Transpiler {
503
781
  }
504
782
  const transpiler = new Transpiler();
505
783
  exports.registerTransformer = transpiler.registerTransformer.bind(transpiler);
506
- const transpileMarkup = (markup, isPartOfPrefab, splitCode) => {
784
+ const transpileMarkup = (markup, isPartOfPrefab, splitCode, variables) => {
507
785
  const parsed = (0, node_html_parser_1.parse)(markup, {
508
786
  blockTextElements: {},
509
787
  });
@@ -514,6 +792,28 @@ const transpileMarkup = (markup, isPartOfPrefab, splitCode) => {
514
792
  prefabs: [],
515
793
  imports: [],
516
794
  };
795
+ // Create a map of ModelVariables (wm.Variable category) with twoWayBinding: true
796
+ // Only ModelVariables support twoWayBinding, other variable types don't
797
+ const twoWayBindingVariables = new Set();
798
+ if (variables) {
799
+ Object.values(variables).forEach((v) => {
800
+ // Only check twoWayBinding for ModelVariables (category === "wm.Variable")
801
+ // These are the only variables that support two-way binding
802
+ if (v.category === "wm.Variable" && v.twoWayBinding === true && v.name) {
803
+ twoWayBindingVariables.add(v.name);
804
+ }
805
+ });
806
+ }
807
+ // Create initial context with twoWayBinding variables info to pass down the tree
808
+ const initialContext = new TranspilationContext({
809
+ result: finalResult,
810
+ isPartOfPrefab: isPartOfPrefab,
811
+ transformer: {},
812
+ props: [],
813
+ data: {
814
+ twoWayBindingVariables: twoWayBindingVariables,
815
+ },
816
+ });
517
817
  // Process all child nodes (including text nodes that come before the main element)
518
818
  parsed.childNodes.forEach((node) => {
519
819
  if (node.nodeType === node_html_parser_1.NodeType.TEXT_NODE) {
@@ -524,7 +824,7 @@ const transpileMarkup = (markup, isPartOfPrefab, splitCode) => {
524
824
  }
525
825
  else if (node.nodeType === node_html_parser_1.NodeType.ELEMENT_NODE) {
526
826
  transpiler.preTranspile(node);
527
- const result = transpiler.transpile(node, isPartOfPrefab, splitCode);
827
+ const result = transpiler.transpile(node, isPartOfPrefab, splitCode, initialContext);
528
828
  finalResult.markup += result.markup;
529
829
  finalResult.components = { ...finalResult.components, ...result.components };
530
830
  finalResult.partials.push(...result.partials);
@@ -541,12 +841,49 @@ const transpileElement = (element, isPartOfPrefab, splitCode = true, oldContext)
541
841
  return transpiler.transpile(element, isPartOfPrefab, splitCode, oldContext);
542
842
  };
543
843
  exports.transpileElement = transpileElement;
844
+ /**
845
+ * Decodes HTML entities in a string
846
+ * Handles common entities like &quot;, &amp;, &lt;, &gt;, &apos;
847
+ */
848
+ const decodeHTMLEntities = (text) => {
849
+ const entities = {
850
+ "&quot;": '"',
851
+ "&amp;": "&",
852
+ "&lt;": "<",
853
+ "&gt;": ">",
854
+ "&apos;": "'",
855
+ };
856
+ return text.replace(/&[a-z]+;/g, entity => entities[entity] || entity);
857
+ };
858
+ /**
859
+ * Transforms $[...] syntax to proper property access
860
+ * Examples:
861
+ * $[location] -> $item.location
862
+ * $[data[$i].locationDesc] -> $item.locationDesc (removes data[$i]. prefix)
863
+ * $[fragment.assetType] -> $item.assetType (removes fragment. prefix)
864
+ */
865
+ const transformDollarBracketSyntax = (expression) => {
866
+ // First, remove data[$i]. prefixes from within $[...] brackets
867
+ expression = expression.replace(/\$\[data\[\$i\]\./g, "$[");
868
+ // Transform $[property.path] to $item.property.path
869
+ expression = expression.replace(/\$\[([^\]]+)\]/g, (match, property) => {
870
+ // Remove data[index]. and fragment. prefixes
871
+ property = property.replace(/data\[\d+\]\./g, "").replace(/^fragment\./, "");
872
+ return `$item.${property}`;
873
+ });
874
+ return expression;
875
+ };
544
876
  const createExpression = (element, attrName) => {
545
877
  let displayexpression = element.attrs[attrName];
546
878
  if (displayexpression === null || displayexpression === void 0 ? void 0 : displayexpression.startsWith("bind:")) {
547
- displayexpression = displayexpression.substring(5);
548
- displayexpression = displayexpression.replace(/data\[\d+\]\./g, "");
549
- displayexpression = displayexpression.replace(/$\[(.*?)\]/g, "$['$1']");
879
+ displayexpression = displayexpression.substring(5).trim();
880
+ // Remove trailing semicolons (common in WaveMaker expressions but invalid in JSX)
881
+ displayexpression = displayexpression.replace(/;+$/, "");
882
+ // Decode HTML entities like &quot; to "
883
+ displayexpression = decodeHTMLEntities(displayexpression);
884
+ // Transform $[...] syntax to proper property access
885
+ displayexpression = transformDollarBracketSyntax(displayexpression);
886
+ // Replace fragment references with $item
550
887
  displayexpression = displayexpression.replace(/fragment\.\$item/g, "$item");
551
888
  displayexpression = displayexpression.replace(/fragment\.\$index/g, "$index");
552
889
  displayexpression = displayexpression.replace(/fragment\.\$length/g, "$length");
@@ -560,12 +897,20 @@ const createExpression = (element, attrName) => {
560
897
  }
561
898
  // Transform fragment.name -> $item.name for arrow functions with $item
562
899
  displayexpression = displayexpression.replace(/fragment\.([a-zA-Z_$][a-zA-Z0-9_$]*)/g, "$item.$1");
900
+ // Add optional chaining for all property access (e.g., $item.App.appLocale -> $item?.App?.appLocale)
901
+ // Use negative lookbehind to avoid double optional chaining (??)
902
+ displayexpression = displayexpression.replace(/(?<!\?)\.([a-zA-Z_$][a-zA-Z0-9_$]*)/g, "?.$1");
903
+ displayexpression = displayexpression.replace(/([a-zA-Z0-9_$\]])\[/g, "$1?.[");
563
904
  const exp = (0, lodash_1.includes)(displayexpression, "(") ? `${displayexpression}` : displayexpression;
564
905
  if (displayexpression.indexOf("$index") >= 0) {
565
906
  return `{($item, $index, $length) => (${exp})}`;
566
907
  }
567
908
  return `{($item) => (${exp})}`;
568
909
  }
910
+ if ((0, utils_1.isJSExpression)(displayexpression)) {
911
+ const transformedExp = (0, bind_ex_transformer_1.default)(displayexpression, "$item");
912
+ return `{($item) => (${transformedExp})}`;
913
+ }
569
914
  };
570
915
  exports.createExpression = createExpression;
571
916
  const preTranspileHtmlElement = (e) => {