@wavemaker-ai/react-codegen 1.0.0-rc.647469 → 1.0.0-rc.647499
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/command.js +6 -1
- package/command.js.map +1 -1
- package/dist/transpiler/index.d.mts +62 -0
- package/dist/transpiler/index.mjs +47857 -0
- package/dist/transpiler/index.mjs.map +1 -0
- package/dist/transpiler/wm-styles.css +1502 -0
- package/package-lock.json +1144 -254
- package/package.json +21 -3
- package/src/app.generator.js +12 -10
- package/src/app.generator.js.map +1 -1
- package/src/gen-app-override-css.js +7 -7
- package/src/gen-app-override-css.js.map +1 -1
- package/src/handlebar-helpers.js +8 -356
- package/src/handlebar-helpers.js.map +1 -1
- package/src/transpile/bind.ex.transformer.js +13 -7
- package/src/transpile/bind.ex.transformer.js.map +1 -1
- package/src/transpile/components/advanced/carousel.transformer.js +1 -2
- package/src/transpile/components/advanced/carousel.transformer.js.map +1 -1
- package/src/transpile/components/advanced/login.transformer.js +1 -2
- package/src/transpile/components/advanced/login.transformer.js.map +1 -1
- package/src/transpile/components/container/accordion-pane.transformer.js +10 -0
- package/src/transpile/components/container/accordion-pane.transformer.js.map +1 -1
- package/src/transpile/components/container/accordion.transformer.js +2 -3
- package/src/transpile/components/container/accordion.transformer.js.map +1 -1
- package/src/transpile/components/container/container.transformer.js +13 -1
- package/src/transpile/components/container/container.transformer.js.map +1 -1
- package/src/transpile/components/container/panel.transformer.js +10 -0
- package/src/transpile/components/container/panel.transformer.js.map +1 -1
- package/src/transpile/components/container/tabpane.transformer.js +10 -0
- package/src/transpile/components/container/tabpane.transformer.js.map +1 -1
- package/src/transpile/components/container/tabs.transformer.js +3 -37
- package/src/transpile/components/container/tabs.transformer.js.map +1 -1
- package/src/transpile/components/container/wizard.transformer.js +3 -4
- package/src/transpile/components/container/wizard.transformer.js.map +1 -1
- package/src/transpile/components/container/wizardstep.transformer.js +25 -0
- package/src/transpile/components/container/wizardstep.transformer.js.map +1 -1
- package/src/transpile/components/data/card/card-content.transformer.js +10 -0
- package/src/transpile/components/data/card/card-content.transformer.js.map +1 -1
- package/src/transpile/components/data/form/form-action.transformer.js +2 -2
- package/src/transpile/components/data/form/form-action.transformer.js.map +1 -1
- package/src/transpile/components/data/form/form-field.transformer.js +2 -3
- package/src/transpile/components/data/form/form-field.transformer.js.map +1 -1
- package/src/transpile/components/data/form/form.transformer.js +6 -7
- package/src/transpile/components/data/form/form.transformer.js.map +1 -1
- package/src/transpile/components/data/list/list-template-transformer.js +10 -1
- package/src/transpile/components/data/list/list-template-transformer.js.map +1 -1
- package/src/transpile/components/data/list/list-transformer.js +24 -45
- package/src/transpile/components/data/list/list-transformer.js.map +1 -1
- package/src/transpile/components/data/live-filter-field.transformer.js +2 -3
- package/src/transpile/components/data/live-filter-field.transformer.js.map +1 -1
- package/src/transpile/components/data/livefilter.transformer.js +6 -7
- package/src/transpile/components/data/livefilter.transformer.js.map +1 -1
- package/src/transpile/components/data/liveform.transformer.js +6 -7
- package/src/transpile/components/data/liveform.transformer.js.map +1 -1
- package/src/transpile/components/data/table/table-column.transformer.js +9 -9
- package/src/transpile/components/data/table/table-column.transformer.js.map +1 -1
- package/src/transpile/components/data/table/table-row.transformer.js +20 -7
- package/src/transpile/components/data/table/table-row.transformer.js.map +1 -1
- package/src/transpile/components/data/table/utils.js +3 -3
- package/src/transpile/components/data/table/utils.js.map +1 -1
- package/src/transpile/components/dialogs/dialog-actions.transformer.js +1 -2
- package/src/transpile/components/dialogs/dialog-actions.transformer.js.map +1 -1
- package/src/transpile/components/dialogs/dialog.transformer.js +1 -1
- package/src/transpile/components/dialogs/dialog.transformer.js.map +1 -1
- package/src/transpile/components/dialogs/login-dialog.transformer.js +2 -3
- package/src/transpile/components/dialogs/login-dialog.transformer.js.map +1 -1
- package/src/transpile/components/layout/footer.transformer.js +27 -10
- package/src/transpile/components/layout/footer.transformer.js.map +1 -1
- package/src/transpile/components/layout/header.transformer.js +29 -12
- package/src/transpile/components/layout/header.transformer.js.map +1 -1
- package/src/transpile/components/layout/leftnav.transformer.js +26 -9
- package/src/transpile/components/layout/leftnav.transformer.js.map +1 -1
- package/src/transpile/components/layout/rightnav.transformer.js +27 -10
- package/src/transpile/components/layout/rightnav.transformer.js.map +1 -1
- package/src/transpile/components/layout/topnav.transformer.js +26 -13
- package/src/transpile/components/layout/topnav.transformer.js.map +1 -1
- package/src/transpile/components/nav/nav-item.transformer.js +1 -2
- package/src/transpile/components/nav/nav-item.transformer.js.map +1 -1
- package/src/transpile/components/nav/nav.transformer.js +1 -2
- package/src/transpile/components/nav/nav.transformer.js.map +1 -1
- package/src/transpile/components/nav/navbar.transformer.js +7 -8
- package/src/transpile/components/nav/navbar.transformer.js.map +1 -1
- package/src/transpile/components/navigation/popover.transformer.js +10 -0
- package/src/transpile/components/navigation/popover.transformer.js.map +1 -1
- package/src/transpile/components/page/partial-container.transformer.js +2 -36
- package/src/transpile/components/page/partial-container.transformer.js.map +1 -1
- package/src/transpile/components/partial/partial-content.transformer.js +56 -0
- package/src/transpile/components/partial/partial-content.transformer.js.map +1 -0
- package/src/transpile/components/prefab/prefab.transformer.js +11 -5
- package/src/transpile/components/prefab/prefab.transformer.js.map +1 -1
- package/src/transpile/components/utils.js +5 -5
- package/src/transpile/components/utils.js.map +1 -1
- package/src/transpile/index.js +65 -0
- package/src/transpile/index.js.map +1 -0
- package/src/transpile/property/property-parser.js +1 -0
- package/src/transpile/property/property-parser.js.map +1 -1
- package/src/transpile/serialize-variables.js +322 -0
- package/src/transpile/serialize-variables.js.map +1 -0
- package/src/transpile/style/split-css-shorthand.js +60 -0
- package/src/transpile/style/split-css-shorthand.js.map +1 -0
- package/src/transpile/transform-markup.js +283 -0
- package/src/transpile/transform-markup.js.map +1 -0
- package/src/transpile/transpile-variables.js +35 -0
- package/src/transpile/transpile-variables.js.map +1 -0
- package/src/transpile/transpile.js +67 -26
- package/src/transpile/transpile.js.map +1 -1
- package/src/transpile/transpiler.js +26 -0
- package/src/transpile/transpiler.js.map +1 -0
- package/src/transpile/variables-template-source.js +6 -0
- package/src/transpile/variables-template-source.js.map +1 -0
- package/src/transpile/variables-template.js +15 -0
- package/src/transpile/variables-template.js.map +1 -0
- package/src/transpile/widget-inline-style-processor.js +14 -44
- package/src/transpile/widget-inline-style-processor.js.map +1 -1
- package/src/utils.browser.js +853 -0
- package/src/utils.browser.js.map +1 -0
- package/src/utils.js +141 -87
- package/src/utils.js.map +1 -1
- package/src/variables/variable.transformer.js +15 -0
- package/src/variables/variable.transformer.js.map +1 -1
- package/templates/component/component.hbs +4 -17
- package/templates/component/partial.hbs +5 -14
- package/templates/project/app/client.layout.tsx +4 -4
- package/templates/project/app/components.css +1 -5
- package/templates/project/app/globals.css +1 -153
- package/templates/project/app/widgetInlineStylesOverride.css +351 -0
- package/templates/project/app/wm-globals.css +152 -0
- package/templates/project/app/wm-styles.css +4 -0
- package/templates/project/package.json +7 -4
|
@@ -0,0 +1,853 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractScriptAndLinkTags = exports.extractInteractiveContainerDiv = exports.htmlToJsx = exports.convertStyleStringToObject = exports.OVERRIDE_TOKENS_PATH = exports.APP_OVERRIDE_CSS_PATH = exports.DESIGN_TOKENS_DIR_NAME = exports.htmlElements = exports.fixURLPathAndScope = exports.scopeCssUnderWmApp = exports.modifyExpression = exports.convertHyphenatedPropsToBracketNotation = exports.transformAppLocale = exports.fixURLPath = exports.addOptionalChaining = exports.addOptionalChainingToPropertyPath = void 0;
|
|
4
|
+
exports.isValid = isValid;
|
|
5
|
+
exports.isJSExpression = isJSExpression;
|
|
6
|
+
const parser_1 = require("@babel/parser");
|
|
7
|
+
const node_html_parser_1 = require("node-html-parser");
|
|
8
|
+
function isValid(expr) {
|
|
9
|
+
try {
|
|
10
|
+
(0, parser_1.parse)(expr, { sourceType: "module", plugins: ["optionalChaining"] });
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
catch (_a) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function isJSExpression(expr) {
|
|
18
|
+
try {
|
|
19
|
+
(0, parser_1.parseExpression)(expr, { sourceType: "module", plugins: ["optionalChaining"] });
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
catch (_a) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Finds the closing `]` for `[` at `openIdx` using bracket nesting depth.
|
|
28
|
+
* Used so property paths inside computed keys (`dataSet[outer[inner]]`) are not truncated.
|
|
29
|
+
*/
|
|
30
|
+
const findMatchingBracketEnd = (str, openIdx) => {
|
|
31
|
+
if (openIdx >= str.length || str[openIdx] !== "[")
|
|
32
|
+
return -1;
|
|
33
|
+
let depth = 0;
|
|
34
|
+
for (let p = openIdx; p < str.length; p++) {
|
|
35
|
+
const c = str[p];
|
|
36
|
+
if (c === "[")
|
|
37
|
+
depth++;
|
|
38
|
+
else if (c === "]") {
|
|
39
|
+
depth--;
|
|
40
|
+
if (depth === 0)
|
|
41
|
+
return p;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return -1;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Extends a property-access path from `start` (must point at the first char of an identifier):
|
|
48
|
+
* `a.b[c[d]].e` — includes balanced bracket segments (not `\[[^\]]+\]`).
|
|
49
|
+
*/
|
|
50
|
+
const extendPropertyAccessPath = (str, start) => {
|
|
51
|
+
let i = start;
|
|
52
|
+
if (i >= str.length || !/[a-zA-Z_$]/.test(str[i]))
|
|
53
|
+
return null;
|
|
54
|
+
i++;
|
|
55
|
+
while (i < str.length && /[a-zA-Z0-9_$]/.test(str[i]))
|
|
56
|
+
i++;
|
|
57
|
+
let end = i;
|
|
58
|
+
while (end < str.length) {
|
|
59
|
+
if (str[end] === ".") {
|
|
60
|
+
if (end + 1 >= str.length || !/[a-zA-Z_$]/.test(str[end + 1]))
|
|
61
|
+
break;
|
|
62
|
+
let j = end + 2;
|
|
63
|
+
while (j < str.length && /[a-zA-Z0-9_$]/.test(str[j]))
|
|
64
|
+
j++;
|
|
65
|
+
end = j;
|
|
66
|
+
}
|
|
67
|
+
else if (str[end] === "[") {
|
|
68
|
+
const rb = findMatchingBracketEnd(str, end);
|
|
69
|
+
if (rb === -1)
|
|
70
|
+
break;
|
|
71
|
+
end = rb + 1;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return { end, text: str.substring(start, end) };
|
|
78
|
+
};
|
|
79
|
+
const collectPropertyPathMatches = (searchStr) => {
|
|
80
|
+
const matches = [];
|
|
81
|
+
let idx = 0;
|
|
82
|
+
while (idx < searchStr.length) {
|
|
83
|
+
const ch = searchStr[idx];
|
|
84
|
+
if (/[a-zA-Z_$]/.test(ch)) {
|
|
85
|
+
const prev = idx > 0 ? searchStr[idx - 1] : "";
|
|
86
|
+
const prevIsIdent = /[a-zA-Z0-9_$]/.test(prev);
|
|
87
|
+
if (!prevIsIdent) {
|
|
88
|
+
const ext = extendPropertyAccessPath(searchStr, idx);
|
|
89
|
+
if (ext && ext.text.length > 0) {
|
|
90
|
+
matches.push({ start: idx, end: ext.end, text: ext.text });
|
|
91
|
+
idx = ext.end;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
idx++;
|
|
97
|
+
}
|
|
98
|
+
return matches;
|
|
99
|
+
};
|
|
100
|
+
const addOptionalChainingToPropertyPath = (propertyPath) => {
|
|
101
|
+
var _a;
|
|
102
|
+
// Split the path into meaningful parts while preserving structure
|
|
103
|
+
const parts = [];
|
|
104
|
+
let current = "";
|
|
105
|
+
let i = 0;
|
|
106
|
+
// Handle the first part specially (no optional chaining at the start)
|
|
107
|
+
while (i < propertyPath.length && propertyPath[i] !== "." && propertyPath[i] !== "[") {
|
|
108
|
+
current += propertyPath[i];
|
|
109
|
+
i++;
|
|
110
|
+
}
|
|
111
|
+
if (current) {
|
|
112
|
+
parts.push({ type: "prop", content: current });
|
|
113
|
+
current = "";
|
|
114
|
+
}
|
|
115
|
+
while (i < propertyPath.length) {
|
|
116
|
+
if (propertyPath[i] === ".") {
|
|
117
|
+
i++; // Skip the dot
|
|
118
|
+
current = "";
|
|
119
|
+
while (i < propertyPath.length && propertyPath[i] !== "." && propertyPath[i] !== "[") {
|
|
120
|
+
current += propertyPath[i];
|
|
121
|
+
i++;
|
|
122
|
+
}
|
|
123
|
+
if (current) {
|
|
124
|
+
parts.push({ type: "prop", content: `.${current}` });
|
|
125
|
+
current = "";
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else if (propertyPath[i] === "[") {
|
|
129
|
+
i++; // Skip the opening bracket
|
|
130
|
+
let bracketContent = "";
|
|
131
|
+
let nestedBracketLevel = 0;
|
|
132
|
+
// Handle nested brackets properly
|
|
133
|
+
while (i < propertyPath.length) {
|
|
134
|
+
if (propertyPath[i] === "[") {
|
|
135
|
+
nestedBracketLevel++;
|
|
136
|
+
bracketContent += propertyPath[i];
|
|
137
|
+
}
|
|
138
|
+
else if (propertyPath[i] === "]") {
|
|
139
|
+
if (nestedBracketLevel === 0) {
|
|
140
|
+
break; // Found the matching closing bracket
|
|
141
|
+
}
|
|
142
|
+
nestedBracketLevel--;
|
|
143
|
+
bracketContent += propertyPath[i];
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
bracketContent += propertyPath[i];
|
|
147
|
+
}
|
|
148
|
+
i++;
|
|
149
|
+
}
|
|
150
|
+
if (i < propertyPath.length) {
|
|
151
|
+
parts.push({ type: "bracket", content: bracketContent });
|
|
152
|
+
i++; // Skip the closing bracket
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
i++; // Skip any unexpected characters
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Helper to process only property paths within bracket content, preserving bracket structure
|
|
160
|
+
const processBracketContent = (content) => {
|
|
161
|
+
const trimmed = content.trim();
|
|
162
|
+
// Skip if it's a string literal or numeric index
|
|
163
|
+
if (trimmed.startsWith("'") || trimmed.startsWith('"'))
|
|
164
|
+
return content;
|
|
165
|
+
if (/^\d+$/.test(trimmed))
|
|
166
|
+
return content;
|
|
167
|
+
// If it contains nested brackets, process property paths within them
|
|
168
|
+
if (trimmed.includes("[")) {
|
|
169
|
+
// Find property paths (identifier.identifier) and process only those parts
|
|
170
|
+
// Example: "data[Widgets.form.value]" -> "data[Widgets?.form?.value]"
|
|
171
|
+
// Example: "items[0]" -> "items[0]" (no change)
|
|
172
|
+
// Use regex to find and replace property paths, preserving bracket structure
|
|
173
|
+
const propertyPathRegex = /([a-zA-Z_$][a-zA-Z0-9_$]*\.[a-zA-Z_$][a-zA-Z0-9_$]*(?:\.[a-zA-Z_$][a-zA-Z0-9_$]*)*)/g;
|
|
174
|
+
return trimmed.replace(propertyPathRegex, match => {
|
|
175
|
+
// Process only the property path part (the part with dots)
|
|
176
|
+
return (0, exports.addOptionalChainingToPropertyPath)(match);
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
// Check if it's a property path (contains dots after identifier)
|
|
180
|
+
if (/^[a-zA-Z_$][a-zA-Z0-9_$]*\.[a-zA-Z_$]/.test(trimmed)) {
|
|
181
|
+
return (0, exports.addOptionalChainingToPropertyPath)(content);
|
|
182
|
+
}
|
|
183
|
+
// Simple identifier or other - leave as is
|
|
184
|
+
return content;
|
|
185
|
+
};
|
|
186
|
+
// Add optional chaining
|
|
187
|
+
let result = ((_a = parts[0]) === null || _a === void 0 ? void 0 : _a.content) || "";
|
|
188
|
+
for (let j = 1; j < parts.length; j++) {
|
|
189
|
+
const part = parts[j];
|
|
190
|
+
if (part.type === "bracket") {
|
|
191
|
+
// Process bracket content - only property paths get optional chaining
|
|
192
|
+
const processedContent = processBracketContent(part.content);
|
|
193
|
+
result += `?.[${processedContent}]`;
|
|
194
|
+
}
|
|
195
|
+
else if (part.content.startsWith(".")) {
|
|
196
|
+
result += `?${part.content}`;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return result;
|
|
200
|
+
};
|
|
201
|
+
exports.addOptionalChainingToPropertyPath = addOptionalChainingToPropertyPath;
|
|
202
|
+
const addOptionalChaining = (bindPath, options = {}) => {
|
|
203
|
+
const { targetPattern, addFallback = false, fallbackValue = '""', detectFunctionCalls = false, } = options;
|
|
204
|
+
// Helper to find balanced parentheses
|
|
205
|
+
const findBalancedParens = (str, start) => {
|
|
206
|
+
let depth = 0;
|
|
207
|
+
for (let i = start; i < str.length; i++) {
|
|
208
|
+
if (str[i] === "(")
|
|
209
|
+
depth++;
|
|
210
|
+
else if (str[i] === ")") {
|
|
211
|
+
depth--;
|
|
212
|
+
if (depth === 0)
|
|
213
|
+
return i;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return -1;
|
|
217
|
+
};
|
|
218
|
+
// Step 1: Extract and protect string literals
|
|
219
|
+
const stringLiterals = [];
|
|
220
|
+
let protectedExpression = bindPath.replace(/"[^"]*"|'[^']*'/g, match => {
|
|
221
|
+
const placeholder = `__STRING_${stringLiterals.length}__`;
|
|
222
|
+
stringLiterals.push(match);
|
|
223
|
+
return placeholder;
|
|
224
|
+
});
|
|
225
|
+
// Step 1.5: Extract and protect function arguments (including arrow functions)
|
|
226
|
+
// This prevents processing property paths inside function arguments
|
|
227
|
+
const functionArgs = [];
|
|
228
|
+
let argProtectedExpression = protectedExpression;
|
|
229
|
+
// Find all function calls and protect their arguments
|
|
230
|
+
let funcArgIndex = 0;
|
|
231
|
+
let i = 0;
|
|
232
|
+
while (i < argProtectedExpression.length) {
|
|
233
|
+
if (argProtectedExpression[i] === "(") {
|
|
234
|
+
const parenStart = i;
|
|
235
|
+
const parenEnd = findBalancedParens(argProtectedExpression, i);
|
|
236
|
+
if (parenEnd !== -1) {
|
|
237
|
+
const argsContent = argProtectedExpression.substring(parenStart + 1, parenEnd);
|
|
238
|
+
// Only protect if there's actual content (not empty parentheses)
|
|
239
|
+
if (argsContent.trim()) {
|
|
240
|
+
const placeholder = `__FUNCARG_${funcArgIndex}__`;
|
|
241
|
+
functionArgs.push(argsContent);
|
|
242
|
+
argProtectedExpression =
|
|
243
|
+
argProtectedExpression.substring(0, parenStart + 1) +
|
|
244
|
+
placeholder +
|
|
245
|
+
argProtectedExpression.substring(parenEnd);
|
|
246
|
+
funcArgIndex++;
|
|
247
|
+
i = parenStart + placeholder.length + 1;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
i++;
|
|
253
|
+
}
|
|
254
|
+
// Step 2: Process property access patterns, handling function calls
|
|
255
|
+
// Default matcher must use balanced `[`…`]` so nested computed keys do not truncate
|
|
256
|
+
// (regex `\[[^\]]+\]` breaks on `dataSet[foo[bar].baz]`).
|
|
257
|
+
let result = argProtectedExpression;
|
|
258
|
+
let matches = [];
|
|
259
|
+
if (targetPattern) {
|
|
260
|
+
targetPattern.lastIndex = 0;
|
|
261
|
+
let match;
|
|
262
|
+
while ((match = targetPattern.exec(argProtectedExpression)) !== null) {
|
|
263
|
+
matches.push({
|
|
264
|
+
start: match.index,
|
|
265
|
+
end: match.index + match[0].length,
|
|
266
|
+
text: match[0],
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
matches = collectPropertyPathMatches(argProtectedExpression);
|
|
272
|
+
}
|
|
273
|
+
// Process matches in reverse order to maintain indices
|
|
274
|
+
for (let i = matches.length - 1; i >= 0; i--) {
|
|
275
|
+
const m = matches[i];
|
|
276
|
+
let processed = (0, exports.addOptionalChainingToPropertyPath)(m.text);
|
|
277
|
+
// Check if there's a function call immediately after this match
|
|
278
|
+
const afterMatch = argProtectedExpression.substring(m.end);
|
|
279
|
+
const funcCallMatch = afterMatch.match(/^\s*\(/);
|
|
280
|
+
if (funcCallMatch) {
|
|
281
|
+
// Find the closing parenthesis
|
|
282
|
+
const parenEnd = findBalancedParens(argProtectedExpression, m.end + funcCallMatch[0].length - 1);
|
|
283
|
+
if (parenEnd !== -1) {
|
|
284
|
+
const funcCall = argProtectedExpression.substring(m.end, parenEnd + 1);
|
|
285
|
+
processed += funcCall;
|
|
286
|
+
// Check if there's property access after the function call
|
|
287
|
+
const afterFunc = argProtectedExpression.substring(parenEnd + 1);
|
|
288
|
+
const propAccessMatch = afterFunc.match(/^\s*\.([a-zA-Z_$][a-zA-Z0-9_$]*)/);
|
|
289
|
+
if (propAccessMatch) {
|
|
290
|
+
processed += `?.${propAccessMatch[1]}`;
|
|
291
|
+
// Update the match end to include the function call and property
|
|
292
|
+
matches[i].end = parenEnd + 1 + propAccessMatch[0].length;
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
matches[i].end = parenEnd + 1;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// Replace in result
|
|
300
|
+
result = result.substring(0, m.start) + processed + result.substring(m.end);
|
|
301
|
+
}
|
|
302
|
+
// Step 3: Add optional chaining after closing parentheses/brackets that are followed by property access
|
|
303
|
+
// This handles cases like:
|
|
304
|
+
// - obj.method().prop
|
|
305
|
+
// - (expr | pipe)[0].url
|
|
306
|
+
// - (expr)[0].url
|
|
307
|
+
result = result.replace(/(\))\s*\.([a-zA-Z_$][a-zA-Z0-9_$]*)/g, "$1?.$2");
|
|
308
|
+
result = result.replace(/(\))\s*\[/g, "$1?.[");
|
|
309
|
+
result = result.replace(/(\])\s*\.([a-zA-Z_$][a-zA-Z0-9_$]*)/g, "$1?.$2");
|
|
310
|
+
result = result.replace(/(\])\s*\[/g, "$1?.[");
|
|
311
|
+
// Step 4: Restore function arguments and process property paths in them
|
|
312
|
+
functionArgs.forEach((arg, index) => {
|
|
313
|
+
let processedArg = arg;
|
|
314
|
+
// Process arrow function bodies (parts after =>)
|
|
315
|
+
const arrowMatch = arg.match(/^([^=]*)=>\s*(.+)$/);
|
|
316
|
+
if (arrowMatch) {
|
|
317
|
+
const [, params, body] = arrowMatch;
|
|
318
|
+
// Process property paths in the arrow function body
|
|
319
|
+
const processedBody = (0, exports.addOptionalChaining)(body);
|
|
320
|
+
processedArg = `${params}=> ${processedBody}`;
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
// Not an arrow function - recursively process with addOptionalChaining
|
|
324
|
+
// This handles nested brackets properly
|
|
325
|
+
processedArg = (0, exports.addOptionalChaining)(arg);
|
|
326
|
+
}
|
|
327
|
+
result = result.replace(`__FUNCARG_${index}__`, processedArg);
|
|
328
|
+
});
|
|
329
|
+
// Step 5: Restore string literals
|
|
330
|
+
stringLiterals.forEach((literal, index) => {
|
|
331
|
+
result = result.replace(`__STRING_${index}__`, literal);
|
|
332
|
+
});
|
|
333
|
+
// Step 6: Add fallback handling if requested
|
|
334
|
+
if (addFallback) {
|
|
335
|
+
if (detectFunctionCalls) {
|
|
336
|
+
const hasFunctionCall = /\([^)]*\)\s*$/.test(result);
|
|
337
|
+
if (!hasFunctionCall) {
|
|
338
|
+
result = `(${result} || ${fallbackValue})`;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
result = `(${result} || ${fallbackValue})`;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return result;
|
|
346
|
+
};
|
|
347
|
+
exports.addOptionalChaining = addOptionalChaining;
|
|
348
|
+
const fixURLPath = (file_content, basePath, isAppCss) => {
|
|
349
|
+
// Fix resources URLs
|
|
350
|
+
let updatedContent = file_content.replace(/url\((['"]?)resources/g, `url($1${basePath || ""}/resources`);
|
|
351
|
+
// Fix font URLs and other URLs that need leading slash
|
|
352
|
+
// This regex matches url() with single or double quotes that don't start with /, http, or data:
|
|
353
|
+
if (isAppCss) {
|
|
354
|
+
updatedContent = updatedContent.replace(/url\((['"])(?!\/|http|data:)([^'"]+)\1\)/g, (match, quote, url) => {
|
|
355
|
+
// Add leading slash if the URL doesn't already have one
|
|
356
|
+
// Check if URL already starts with / (absolute path)
|
|
357
|
+
const fixedUrl = url.startsWith("/") ? url : `/${url}`;
|
|
358
|
+
return `url(${quote}${fixedUrl}${quote})`;
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
return updatedContent;
|
|
362
|
+
};
|
|
363
|
+
exports.fixURLPath = fixURLPath;
|
|
364
|
+
const transformAppLocale = (exp) => {
|
|
365
|
+
if (exp.startsWith("fragment?.Prefab") || exp.startsWith("fragment.Prefab")) {
|
|
366
|
+
exp = exp.replace("fragment?.Prefab", "fragment");
|
|
367
|
+
exp = exp.replace("fragment.Prefab", "fragment");
|
|
368
|
+
}
|
|
369
|
+
// Updated regex to capture the optional 'fragment.' prefix
|
|
370
|
+
const appLocaleRegex = /(fragment\.)?appLocale(?:\.messages)?(?:\[(['"])([^'"]+)\2\]|\.([a-zA-Z0-9_]+))/g;
|
|
371
|
+
// We use a replacer function with .replace() to have full control over the output.
|
|
372
|
+
// This function is called for every match found by the regex.
|
|
373
|
+
const replacer = (fullMatch, fragmentPrefix, quoteType, keyFromBrackets, keyFromDot) => {
|
|
374
|
+
// The key will either be from the bracket capture group or the dot capture group.
|
|
375
|
+
const key = keyFromBrackets || keyFromDot;
|
|
376
|
+
if (key) {
|
|
377
|
+
// If there's a fragment prefix, include it in the formatMessage call
|
|
378
|
+
if (fragmentPrefix) {
|
|
379
|
+
return `formatMessage(fragment, '${key}')`;
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
return `formatMessage('${key}')`;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// Fallback to return the original match if key extraction fails (unlikely)
|
|
386
|
+
return fullMatch;
|
|
387
|
+
};
|
|
388
|
+
// Apply the transformation
|
|
389
|
+
const expression = exp.replace(appLocaleRegex, replacer);
|
|
390
|
+
return expression;
|
|
391
|
+
};
|
|
392
|
+
exports.transformAppLocale = transformAppLocale;
|
|
393
|
+
/**
|
|
394
|
+
* Converts property paths with hyphens to bracket notation for valid JavaScript.
|
|
395
|
+
* e.g. Actions.goToPage_test-page.invoke() -> Actions["goToPage_test-page"].invoke()
|
|
396
|
+
* Hyphens in identifiers are invalid in JS (parsed as subtraction), so we use bracket notation.
|
|
397
|
+
*/
|
|
398
|
+
const convertHyphenatedPropsToBracketNotation = (expr) => {
|
|
399
|
+
if (!expr || typeof expr !== "string")
|
|
400
|
+
return expr;
|
|
401
|
+
// Match .identifier where identifier contains hyphen (e.g. .goToPage_test-page, .my-var)
|
|
402
|
+
return expr.replace(/\.([a-zA-Z_$][a-zA-Z0-9_$]*-[a-zA-Z_$][a-zA-Z0-9_$\-]*)/g, (_, propName) => {
|
|
403
|
+
return `["${propName}"]`;
|
|
404
|
+
});
|
|
405
|
+
};
|
|
406
|
+
exports.convertHyphenatedPropsToBracketNotation = convertHyphenatedPropsToBracketNotation;
|
|
407
|
+
const modifyExpression = (exp) => {
|
|
408
|
+
if (exp.startsWith('"http') || exp.startsWith('"/') || exp.startsWith("{")) {
|
|
409
|
+
return exp;
|
|
410
|
+
}
|
|
411
|
+
if (!isValid(exp))
|
|
412
|
+
return `""`;
|
|
413
|
+
if (exp.includes("appLocale")) {
|
|
414
|
+
const transformedExp = (0, exports.transformAppLocale)(exp);
|
|
415
|
+
return (0, exports.addOptionalChaining)(transformedExp);
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
return (0, exports.addOptionalChaining)(exp);
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
exports.modifyExpression = modifyExpression;
|
|
422
|
+
const scopeCssUnderWmApp = (cssContent) => {
|
|
423
|
+
if (!cssContent || cssContent.trim() === "") {
|
|
424
|
+
return cssContent;
|
|
425
|
+
}
|
|
426
|
+
// Split CSS into rules while preserving structure
|
|
427
|
+
const rules = [];
|
|
428
|
+
let currentRule = "";
|
|
429
|
+
let inComment = false;
|
|
430
|
+
let inString = false;
|
|
431
|
+
let stringChar = "";
|
|
432
|
+
let braceLevel = 0;
|
|
433
|
+
for (let i = 0; i < cssContent.length; i++) {
|
|
434
|
+
const char = cssContent[i];
|
|
435
|
+
const nextChar = cssContent[i + 1];
|
|
436
|
+
// Handle comments
|
|
437
|
+
if (!inString && char === "/" && nextChar === "*") {
|
|
438
|
+
inComment = true;
|
|
439
|
+
currentRule += char;
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
if (inComment && char === "*" && nextChar === "/") {
|
|
443
|
+
inComment = false;
|
|
444
|
+
currentRule += char;
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
if (inComment) {
|
|
448
|
+
currentRule += char;
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
// Handle strings
|
|
452
|
+
if (!inString && (char === '"' || char === "'")) {
|
|
453
|
+
inString = true;
|
|
454
|
+
stringChar = char;
|
|
455
|
+
currentRule += char;
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if (inString && char === stringChar && cssContent[i - 1] !== "\\") {
|
|
459
|
+
inString = false;
|
|
460
|
+
stringChar = "";
|
|
461
|
+
currentRule += char;
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
if (inString) {
|
|
465
|
+
currentRule += char;
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
// Handle braces to track rule boundaries
|
|
469
|
+
if (char === "{") {
|
|
470
|
+
braceLevel++;
|
|
471
|
+
currentRule += char;
|
|
472
|
+
}
|
|
473
|
+
else if (char === "}") {
|
|
474
|
+
braceLevel--;
|
|
475
|
+
currentRule += char;
|
|
476
|
+
// If we're back to level 0, we've completed a rule
|
|
477
|
+
if (braceLevel === 0) {
|
|
478
|
+
rules.push(currentRule.trim());
|
|
479
|
+
currentRule = "";
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
currentRule += char;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
// Add any remaining content
|
|
487
|
+
if (currentRule.trim()) {
|
|
488
|
+
rules.push(currentRule.trim());
|
|
489
|
+
}
|
|
490
|
+
// Process each rule
|
|
491
|
+
const processedRules = rules.map(rule => {
|
|
492
|
+
// Skip empty rules or comments
|
|
493
|
+
if (!rule ||
|
|
494
|
+
rule.startsWith("/*") ||
|
|
495
|
+
rule.startsWith("@import") ||
|
|
496
|
+
rule.startsWith("@charset")) {
|
|
497
|
+
return rule;
|
|
498
|
+
}
|
|
499
|
+
// Handle at-rules like @media, @keyframes, etc.
|
|
500
|
+
if (rule.startsWith("@")) {
|
|
501
|
+
return rule;
|
|
502
|
+
}
|
|
503
|
+
// Find the opening brace to separate selector from declarations
|
|
504
|
+
const openBraceIndex = rule.indexOf("{");
|
|
505
|
+
if (openBraceIndex === -1) {
|
|
506
|
+
return rule;
|
|
507
|
+
}
|
|
508
|
+
const selector = rule.substring(0, openBraceIndex).trim();
|
|
509
|
+
const declarations = rule.substring(openBraceIndex);
|
|
510
|
+
// Skip if already scoped under .wm-app .app-page
|
|
511
|
+
if (selector.includes(".wm-app .app-page") ||
|
|
512
|
+
selector.startsWith(".wm-app .app-page") ||
|
|
513
|
+
selector.includes(".wm-app") ||
|
|
514
|
+
selector.startsWith(".wm-app")) {
|
|
515
|
+
return rule;
|
|
516
|
+
}
|
|
517
|
+
// Split multiple selectors (comma-separated)
|
|
518
|
+
const selectors = selector.split(",").map(s => s.trim());
|
|
519
|
+
// Scope each selector under .wm-app .app-page
|
|
520
|
+
const scopedSelectors = selectors.map(sel => {
|
|
521
|
+
if (!sel)
|
|
522
|
+
return sel;
|
|
523
|
+
// Handle pseudo-selectors like :root, :before, :after
|
|
524
|
+
if (sel.startsWith(":")) {
|
|
525
|
+
return sel;
|
|
526
|
+
}
|
|
527
|
+
// Handle html, body tags - wrap them
|
|
528
|
+
if (sel === "html" || sel === "body" || sel.startsWith("html ") || sel.startsWith("body ")) {
|
|
529
|
+
return `.wm-app .app-page ${sel}`;
|
|
530
|
+
}
|
|
531
|
+
// For all other selectors, wrap them under .wm-app .app-page
|
|
532
|
+
return `.wm-app .app-page ${sel}`;
|
|
533
|
+
});
|
|
534
|
+
return `${scopedSelectors.join(", ")} ${declarations}`;
|
|
535
|
+
});
|
|
536
|
+
return processedRules.join("\n");
|
|
537
|
+
};
|
|
538
|
+
exports.scopeCssUnderWmApp = scopeCssUnderWmApp;
|
|
539
|
+
const fixURLPathAndScope = (file_content, basePath, isAppCss) => {
|
|
540
|
+
const urlFixed = (0, exports.fixURLPath)(file_content, basePath, isAppCss);
|
|
541
|
+
return (0, exports.scopeCssUnderWmApp)(urlFixed);
|
|
542
|
+
};
|
|
543
|
+
exports.fixURLPathAndScope = fixURLPathAndScope;
|
|
544
|
+
exports.htmlElements = [
|
|
545
|
+
"header",
|
|
546
|
+
"footer",
|
|
547
|
+
"main",
|
|
548
|
+
"section",
|
|
549
|
+
"article",
|
|
550
|
+
"aside",
|
|
551
|
+
"nav",
|
|
552
|
+
"div",
|
|
553
|
+
"span",
|
|
554
|
+
"p",
|
|
555
|
+
"a",
|
|
556
|
+
"img",
|
|
557
|
+
"button",
|
|
558
|
+
"input",
|
|
559
|
+
"textarea",
|
|
560
|
+
"select",
|
|
561
|
+
"option",
|
|
562
|
+
"label",
|
|
563
|
+
"form",
|
|
564
|
+
"table",
|
|
565
|
+
"tbody",
|
|
566
|
+
"tr",
|
|
567
|
+
"td",
|
|
568
|
+
"th",
|
|
569
|
+
"ul",
|
|
570
|
+
"li",
|
|
571
|
+
"ol",
|
|
572
|
+
"h1",
|
|
573
|
+
"h2",
|
|
574
|
+
"h3",
|
|
575
|
+
"h4",
|
|
576
|
+
"h5",
|
|
577
|
+
"h6",
|
|
578
|
+
"iframe",
|
|
579
|
+
];
|
|
580
|
+
exports.DESIGN_TOKENS_DIR_NAME = "design-tokens";
|
|
581
|
+
exports.APP_OVERRIDE_CSS_PATH = `${exports.DESIGN_TOKENS_DIR_NAME}/app.override.css`;
|
|
582
|
+
exports.OVERRIDE_TOKENS_PATH = `${exports.DESIGN_TOKENS_DIR_NAME}/overrides`;
|
|
583
|
+
const getExistingImportUrls = (layoutContent) => {
|
|
584
|
+
const existingUrls = new Set();
|
|
585
|
+
try {
|
|
586
|
+
const parsed = (0, node_html_parser_1.parse)(layoutContent);
|
|
587
|
+
const traverseNodes = (node) => {
|
|
588
|
+
var _a;
|
|
589
|
+
if (node.nodeType === node_html_parser_1.NodeType.ELEMENT_NODE) {
|
|
590
|
+
const tagName = (_a = node.tagName) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
591
|
+
if (tagName === "script" || tagName === "link") {
|
|
592
|
+
const url = node.getAttribute("src") || node.getAttribute("href") || "";
|
|
593
|
+
if (url) {
|
|
594
|
+
existingUrls.add(url);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
if (node.childNodes) {
|
|
598
|
+
node.childNodes.forEach((child) => {
|
|
599
|
+
traverseNodes(child);
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
parsed.childNodes.forEach((node) => {
|
|
605
|
+
traverseNodes(node);
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
catch (error) {
|
|
609
|
+
// If parsing fails, return empty set
|
|
610
|
+
}
|
|
611
|
+
return existingUrls;
|
|
612
|
+
};
|
|
613
|
+
/**
|
|
614
|
+
* HTML attribute names that need to be converted for JSX compatibility
|
|
615
|
+
*/
|
|
616
|
+
const HTML_TO_JSX_ATTR_MAP = {
|
|
617
|
+
class: "className",
|
|
618
|
+
for: "htmlFor",
|
|
619
|
+
tabindex: "tabIndex",
|
|
620
|
+
readonly: "readOnly",
|
|
621
|
+
autofocus: "autoFocus",
|
|
622
|
+
autoplay: "autoPlay",
|
|
623
|
+
crossorigin: "crossOrigin",
|
|
624
|
+
maxlength: "maxLength",
|
|
625
|
+
minlength: "minLength",
|
|
626
|
+
spellcheck: "spellCheck",
|
|
627
|
+
};
|
|
628
|
+
/**
|
|
629
|
+
* Void elements that must be self-closing in JSX
|
|
630
|
+
*/
|
|
631
|
+
const VOID_ELEMENTS = new Set([
|
|
632
|
+
"area",
|
|
633
|
+
"base",
|
|
634
|
+
"br",
|
|
635
|
+
"col",
|
|
636
|
+
"embed",
|
|
637
|
+
"hr",
|
|
638
|
+
"img",
|
|
639
|
+
"input",
|
|
640
|
+
"link",
|
|
641
|
+
"meta",
|
|
642
|
+
"param",
|
|
643
|
+
"source",
|
|
644
|
+
"track",
|
|
645
|
+
"wbr",
|
|
646
|
+
]);
|
|
647
|
+
/**
|
|
648
|
+
* Converts CSS style string to React style object format
|
|
649
|
+
* Example: "color:#ffffff; font-size:14px" -> {color:"#ffffff",fontSize:"14px"}
|
|
650
|
+
*/
|
|
651
|
+
const convertStyleStringToObject = (styleString) => {
|
|
652
|
+
if (!styleString || styleString.trim() === "") {
|
|
653
|
+
return "{}";
|
|
654
|
+
}
|
|
655
|
+
const styleObject = {};
|
|
656
|
+
styleString.split(";").forEach(rule => {
|
|
657
|
+
const colonIndex = rule.indexOf(":");
|
|
658
|
+
if (colonIndex > 0) {
|
|
659
|
+
const property = rule.substring(0, colonIndex).trim();
|
|
660
|
+
const value = rule.substring(colonIndex + 1).trim();
|
|
661
|
+
if (property && value) {
|
|
662
|
+
const camelCaseProperty = property.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
663
|
+
styleObject[camelCaseProperty] = value;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
return JSON.stringify(styleObject);
|
|
668
|
+
};
|
|
669
|
+
exports.convertStyleStringToObject = convertStyleStringToObject;
|
|
670
|
+
/**
|
|
671
|
+
* Converts HTML string to JSX-compatible format (class->className, style->style object, etc.)
|
|
672
|
+
* @param htmlString The HTML string to convert
|
|
673
|
+
* @returns JSX-compatible string
|
|
674
|
+
*/
|
|
675
|
+
const htmlToJsx = (htmlString) => {
|
|
676
|
+
if (!htmlString || typeof htmlString !== "string") {
|
|
677
|
+
return "";
|
|
678
|
+
}
|
|
679
|
+
try {
|
|
680
|
+
const parsed = (0, node_html_parser_1.parse)(htmlString);
|
|
681
|
+
const nodeToJsx = (node) => {
|
|
682
|
+
var _a, _b;
|
|
683
|
+
if (node.nodeType === node_html_parser_1.NodeType.TEXT_NODE) {
|
|
684
|
+
return node.text || "";
|
|
685
|
+
}
|
|
686
|
+
if (node.nodeType !== node_html_parser_1.NodeType.ELEMENT_NODE) {
|
|
687
|
+
return "";
|
|
688
|
+
}
|
|
689
|
+
const tagName = ((_a = node.tagName) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || "div";
|
|
690
|
+
const isVoid = VOID_ELEMENTS.has(tagName);
|
|
691
|
+
const attrs = [];
|
|
692
|
+
const attributes = node.attributes || node.attrs || {};
|
|
693
|
+
if (Object.keys(attributes).length > 0) {
|
|
694
|
+
for (const [attr, value] of Object.entries(attributes)) {
|
|
695
|
+
if (value == null || value === "")
|
|
696
|
+
continue;
|
|
697
|
+
const attrLower = attr.toLowerCase();
|
|
698
|
+
if (attrLower === "class") {
|
|
699
|
+
attrs.push(`className="${value}"`);
|
|
700
|
+
}
|
|
701
|
+
else if (attrLower === "style") {
|
|
702
|
+
attrs.push(`style={${(0, exports.convertStyleStringToObject)(value)}}`);
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
const jsxAttr = HTML_TO_JSX_ATTR_MAP[attrLower] || attr;
|
|
706
|
+
attrs.push(`${jsxAttr}="${value}"`);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
const attrsStr = attrs.length ? " " + attrs.join(" ") : "";
|
|
711
|
+
if (isVoid) {
|
|
712
|
+
return `<${tagName}${attrsStr} />`;
|
|
713
|
+
}
|
|
714
|
+
let children = "";
|
|
715
|
+
if ((_b = node.childNodes) === null || _b === void 0 ? void 0 : _b.length) {
|
|
716
|
+
children = node.childNodes.map((child) => nodeToJsx(child)).join("");
|
|
717
|
+
}
|
|
718
|
+
return `<${tagName}${attrsStr}>${children}</${tagName}>`;
|
|
719
|
+
};
|
|
720
|
+
return parsed.childNodes.map((node) => nodeToJsx(node)).join("");
|
|
721
|
+
}
|
|
722
|
+
catch (error) {
|
|
723
|
+
console.error("Error converting HTML to JSX:", error);
|
|
724
|
+
return "";
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
exports.htmlToJsx = htmlToJsx;
|
|
728
|
+
/**
|
|
729
|
+
* Extracts the interactive-container div from HTML (with full structure including children)
|
|
730
|
+
* @param htmlString The HTML string to parse
|
|
731
|
+
* @returns The outerHTML of the interactive-container div, or empty string if not found
|
|
732
|
+
*/
|
|
733
|
+
const extractInteractiveContainerDiv = (htmlString) => {
|
|
734
|
+
if (!htmlString || typeof htmlString !== "string") {
|
|
735
|
+
return "";
|
|
736
|
+
}
|
|
737
|
+
try {
|
|
738
|
+
const parsed = (0, node_html_parser_1.parse)(htmlString);
|
|
739
|
+
const findInteractiveContainer = (node) => {
|
|
740
|
+
var _a;
|
|
741
|
+
if (node.nodeType === node_html_parser_1.NodeType.ELEMENT_NODE) {
|
|
742
|
+
const tagName = (_a = node.tagName) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
743
|
+
if (tagName === "div") {
|
|
744
|
+
const className = node.getAttribute("class") || "";
|
|
745
|
+
if (className.split(/\s+/).includes("interactive-container")) {
|
|
746
|
+
return node;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
if (node.childNodes) {
|
|
750
|
+
for (const child of node.childNodes) {
|
|
751
|
+
const found = findInteractiveContainer(child);
|
|
752
|
+
if (found)
|
|
753
|
+
return found;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
return null;
|
|
758
|
+
};
|
|
759
|
+
for (const node of parsed.childNodes) {
|
|
760
|
+
const div = findInteractiveContainer(node);
|
|
761
|
+
if (div) {
|
|
762
|
+
return div.outerHTML;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
catch (error) {
|
|
767
|
+
console.error("Error extracting interactive-container div:", error);
|
|
768
|
+
}
|
|
769
|
+
return "";
|
|
770
|
+
};
|
|
771
|
+
exports.extractInteractiveContainerDiv = extractInteractiveContainerDiv;
|
|
772
|
+
/**
|
|
773
|
+
* Extracts only <script> and <link> tags from an HTML string that:
|
|
774
|
+
* - Have CDN paths (http:// or https:// URLs)
|
|
775
|
+
* - Are NOT from jQuery or Angular libraries
|
|
776
|
+
* - Are NOT already present in the layout file
|
|
777
|
+
* @param htmlString The HTML string to parse
|
|
778
|
+
* @param layoutContent Optional layout file content to check for existing imports
|
|
779
|
+
* @returns A string containing only the extracted <script> and <link> tags
|
|
780
|
+
*/
|
|
781
|
+
const extractScriptAndLinkTags = (htmlString, layoutContent) => {
|
|
782
|
+
if (!htmlString || typeof htmlString !== "string") {
|
|
783
|
+
return "";
|
|
784
|
+
}
|
|
785
|
+
try {
|
|
786
|
+
const parsed = (0, node_html_parser_1.parse)(htmlString);
|
|
787
|
+
const extractedTags = [];
|
|
788
|
+
// Get existing URLs from layout if provided
|
|
789
|
+
const existingUrls = layoutContent ? getExistingImportUrls(layoutContent) : new Set();
|
|
790
|
+
// Patterns to identify jQuery and Angular libraries
|
|
791
|
+
const jqueryPatterns = [/jquery/i, /\bjq\b/i, /jquery-ui/i, /jquery\.min/i];
|
|
792
|
+
const angularPatterns = [/angular/i, /ng-/i, /angularjs/i, /@angular/i];
|
|
793
|
+
// Function to check if a URL is from jQuery or Angular
|
|
794
|
+
const isJQueryOrAngular = (url) => {
|
|
795
|
+
if (!url)
|
|
796
|
+
return false;
|
|
797
|
+
const lowerUrl = url.toLowerCase();
|
|
798
|
+
return (jqueryPatterns.some(pattern => pattern.test(lowerUrl)) ||
|
|
799
|
+
angularPatterns.some(pattern => pattern.test(lowerUrl)));
|
|
800
|
+
};
|
|
801
|
+
// Function to check if a URL is a CDN path (http:// or https://)
|
|
802
|
+
const isCdnPath = (url) => {
|
|
803
|
+
if (!url)
|
|
804
|
+
return false;
|
|
805
|
+
return url.startsWith("http://") || url.startsWith("https://");
|
|
806
|
+
};
|
|
807
|
+
// Function to recursively traverse nodes
|
|
808
|
+
const traverseNodes = (node) => {
|
|
809
|
+
var _a;
|
|
810
|
+
if (node.nodeType === node_html_parser_1.NodeType.ELEMENT_NODE) {
|
|
811
|
+
const tagName = (_a = node.tagName) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
812
|
+
// Extract script and link tags
|
|
813
|
+
if (tagName === "script" || tagName === "link") {
|
|
814
|
+
// Get the URL from src (for script) or href (for link)
|
|
815
|
+
const url = node.getAttribute("src") || node.getAttribute("href") || "";
|
|
816
|
+
// It's a CDN path (http:// or https://)
|
|
817
|
+
if (isCdnPath(url) && !isJQueryOrAngular(url) && !existingUrls.has(url)) {
|
|
818
|
+
// Convert to JSX-compatible format (self-closing for link tags only)
|
|
819
|
+
let jsxTag = node.outerHTML;
|
|
820
|
+
// For link tags, ensure they are self-closing for JSX compatibility
|
|
821
|
+
if (tagName === "link") {
|
|
822
|
+
// Remove any closing tag </link>
|
|
823
|
+
jsxTag = jsxTag.replace(/\s*<\/link>/gi, "");
|
|
824
|
+
// If it doesn't end with />, make it self-closing
|
|
825
|
+
if (!jsxTag.trim().endsWith("/>")) {
|
|
826
|
+
// Replace the closing > with />
|
|
827
|
+
jsxTag = jsxTag.replace(/>\s*$/, " />");
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
extractedTags.push(jsxTag);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
// Recursively process child nodes
|
|
834
|
+
if (node.childNodes) {
|
|
835
|
+
node.childNodes.forEach((child) => {
|
|
836
|
+
traverseNodes(child);
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
// Traverse all nodes in the parsed HTML
|
|
842
|
+
parsed.childNodes.forEach((node) => {
|
|
843
|
+
traverseNodes(node);
|
|
844
|
+
});
|
|
845
|
+
return extractedTags.join("\n");
|
|
846
|
+
}
|
|
847
|
+
catch (error) {
|
|
848
|
+
console.error("Error extracting script and link tags:", error);
|
|
849
|
+
return "";
|
|
850
|
+
}
|
|
851
|
+
};
|
|
852
|
+
exports.extractScriptAndLinkTags = extractScriptAndLinkTags;
|
|
853
|
+
//# sourceMappingURL=utils.browser.js.map
|