@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.
- package/build-react-web.js +0 -19
- package/build-react-web.js.map +1 -1
- package/package-lock.json +136 -132
- package/package.json +2 -2
- package/src/app.generator.js +176 -18
- package/src/app.generator.js.map +1 -1
- package/src/gen-app-override-css.js +1 -1
- package/src/gen-app-override-css.js.map +1 -1
- package/src/handlebar-helpers.js +116 -19
- package/src/handlebar-helpers.js.map +1 -1
- package/src/project.service.js +47 -5
- package/src/project.service.js.map +1 -1
- package/src/transpile/bind.ex.transformer.js +11 -1
- package/src/transpile/bind.ex.transformer.js.map +1 -1
- package/src/transpile/components/basic/richtexteditor.transformer.js +3 -2
- package/src/transpile/components/basic/richtexteditor.transformer.js.map +1 -1
- package/src/transpile/components/basic/search.transformer.js +4 -6
- package/src/transpile/components/basic/search.transformer.js.map +1 -1
- package/src/transpile/components/container/tabpane.transformer.js +1 -1
- package/src/transpile/components/container/tabpane.transformer.js.map +1 -1
- package/src/transpile/components/container/tabs.transformer.js +4 -0
- package/src/transpile/components/container/tabs.transformer.js.map +1 -1
- package/src/transpile/components/data/card/card-content.transformer.js +5 -1
- package/src/transpile/components/data/card/card-content.transformer.js.map +1 -1
- package/src/transpile/components/data/form/form-action.transformer.js +73 -18
- package/src/transpile/components/data/form/form-action.transformer.js.map +1 -1
- package/src/transpile/components/data/form/form-field.transformer.js +46 -11
- package/src/transpile/components/data/form/form-field.transformer.js.map +1 -1
- package/src/transpile/components/data/form/form.transformer.js +53 -17
- package/src/transpile/components/data/form/form.transformer.js.map +1 -1
- package/src/transpile/components/data/list/list-transformer.js +64 -33
- package/src/transpile/components/data/list/list-transformer.js.map +1 -1
- package/src/transpile/components/data/live-filter-field.transformer.js +65 -10
- package/src/transpile/components/data/live-filter-field.transformer.js.map +1 -1
- package/src/transpile/components/data/liveform.transformer.js +11 -4
- package/src/transpile/components/data/liveform.transformer.js.map +1 -1
- package/src/transpile/components/data/table/table-action.transformer.js +6 -2
- package/src/transpile/components/data/table/table-action.transformer.js.map +1 -1
- package/src/transpile/components/data/table/table-column.transformer.js +95 -20
- package/src/transpile/components/data/table/table-column.transformer.js.map +1 -1
- package/src/transpile/components/data/table/table-group.transformer.js +16 -0
- package/src/transpile/components/data/table/table-group.transformer.js.map +1 -0
- package/src/transpile/components/data/table/table-row-action.transformer.js +6 -2
- package/src/transpile/components/data/table/table-row-action.transformer.js.map +1 -1
- package/src/transpile/components/data/table/table-row.transformer.js +7 -4
- package/src/transpile/components/data/table/table-row.transformer.js.map +1 -1
- package/src/transpile/components/data/table/table.transformer.js +29 -1
- package/src/transpile/components/data/table/table.transformer.js.map +1 -1
- package/src/transpile/components/data/table/utils.js +3 -1
- package/src/transpile/components/data/table/utils.js.map +1 -1
- package/src/transpile/components/form/button.transformer.js +12 -1
- package/src/transpile/components/form/button.transformer.js.map +1 -1
- package/src/transpile/components/input/checkbox-set.transformer.js +1 -1
- package/src/transpile/components/input/checkbox-set.transformer.js.map +1 -1
- package/src/transpile/components/input/chips.transformer.js +2 -1
- package/src/transpile/components/input/chips.transformer.js.map +1 -1
- package/src/transpile/components/input/color-picker.transformer.js +2 -1
- package/src/transpile/components/input/color-picker.transformer.js.map +1 -1
- package/src/transpile/components/input/rating.transformer.js +1 -1
- package/src/transpile/components/input/rating.transformer.js.map +1 -1
- package/src/transpile/components/input/select.transformer.js +1 -1
- package/src/transpile/components/input/select.transformer.js.map +1 -1
- package/src/transpile/components/input/text.transformer.js +3 -1
- package/src/transpile/components/input/text.transformer.js.map +1 -1
- package/src/transpile/components/input/upload.transformer.js +19 -0
- package/src/transpile/components/input/upload.transformer.js.map +1 -0
- package/src/transpile/components/nav/navbar.transformer.js +84 -0
- package/src/transpile/components/nav/navbar.transformer.js.map +1 -1
- package/src/transpile/components/navigation/popover.transformer.js +1 -1
- package/src/transpile/components/navigation/popover.transformer.js.map +1 -1
- package/src/transpile/components/page/partial-container.transformer.js +15 -6
- package/src/transpile/components/page/partial-container.transformer.js.map +1 -1
- package/src/transpile/components/prefab/prefab.transformer.js +23 -2
- package/src/transpile/components/prefab/prefab.transformer.js.map +1 -1
- package/src/transpile/components/transform-register.js +4 -0
- package/src/transpile/components/transform-register.js.map +1 -1
- package/src/transpile/components/utils.js +176 -15
- package/src/transpile/components/utils.js.map +1 -1
- package/src/transpile/property/property-parser.js +12 -0
- package/src/transpile/property/property-parser.js.map +1 -1
- package/src/transpile/style.transformer.js +7 -1
- package/src/transpile/style.transformer.js.map +1 -1
- package/src/transpile/transpile.js +370 -25
- package/src/transpile/transpile.js.map +1 -1
- package/src/utils.js +343 -36
- package/src/utils.js.map +1 -1
- package/src/utils.test.js +703 -0
- package/src/utils.test.js.map +1 -0
- package/src/variables/variable.transformer.js +302 -135
- package/src/variables/variable.transformer.js.map +1 -1
- package/templates/component/app.template.hbs +1 -0
- package/templates/component/component.hbs +12 -1
- package/templates/component/partial.hbs +8 -0
- package/templates/project/.env +1 -1
- package/templates/project/app/components.css +16 -15
- package/templates/project/app/globals.css +39 -0
- package/templates/project/app/loading.tsx +1 -1
- package/templates/project/package.json +8 -8
- package/templates/variables.template +21 -2
- 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
|
|
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
|
-
|
|
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
|
|
141
|
-
if (
|
|
142
|
-
result.markup +=
|
|
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
|
|
174
|
-
if (
|
|
175
|
-
result.markup +=
|
|
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
|
-
|
|
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
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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, "<")
|
|
393
642
|
.replace(/>/g, ">");
|
|
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
|
-
|
|
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
|
-
|
|
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 ", &, <, >, '
|
|
847
|
+
*/
|
|
848
|
+
const decodeHTMLEntities = (text) => {
|
|
849
|
+
const entities = {
|
|
850
|
+
""": '"',
|
|
851
|
+
"&": "&",
|
|
852
|
+
"<": "<",
|
|
853
|
+
">": ">",
|
|
854
|
+
"'": "'",
|
|
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
|
-
|
|
549
|
-
displayexpression = displayexpression.replace(
|
|
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 " 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) => {
|