pulse-js-framework 1.7.24 → 1.7.25
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.
|
@@ -25,10 +25,14 @@ export function transformExpression(transformer, node) {
|
|
|
25
25
|
|
|
26
26
|
switch (node.type) {
|
|
27
27
|
case NodeType.Identifier:
|
|
28
|
+
// Props take precedence over state (props are destructured in render scope)
|
|
29
|
+
if (transformer.propVars.has(node.name)) {
|
|
30
|
+
return node.name;
|
|
31
|
+
}
|
|
28
32
|
if (transformer.stateVars.has(node.name)) {
|
|
29
33
|
return `${node.name}.get()`;
|
|
30
34
|
}
|
|
31
|
-
//
|
|
35
|
+
// Other identifiers (actions, imports, etc.) accessed directly
|
|
32
36
|
return node.name;
|
|
33
37
|
|
|
34
38
|
case NodeType.Literal:
|
|
@@ -153,8 +157,13 @@ export function transformExpression(transformer, node) {
|
|
|
153
157
|
*/
|
|
154
158
|
export function transformExpressionString(transformer, exprStr) {
|
|
155
159
|
// Simple transformation: wrap state vars with .get()
|
|
160
|
+
// Props take precedence - don't wrap props with .get()
|
|
156
161
|
let result = exprStr;
|
|
157
162
|
for (const stateVar of transformer.stateVars) {
|
|
163
|
+
// Skip if this var name is also a prop (props shadow state in render scope)
|
|
164
|
+
if (transformer.propVars.has(stateVar)) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
158
167
|
result = result.replace(
|
|
159
168
|
new RegExp(`\\b${stateVar}\\b`, 'g'),
|
|
160
169
|
`${stateVar}.get()`
|
|
@@ -298,32 +298,127 @@ export function transformFocusTrapDirective(transformer, node, indent) {
|
|
|
298
298
|
return `{ ${optionsCode} }`;
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Parse a balanced expression starting from an opening brace
|
|
303
|
+
* Handles nested braces and string literals correctly
|
|
304
|
+
* @param {string} str - The string to parse
|
|
305
|
+
* @param {number} start - Index of the opening brace
|
|
306
|
+
* @returns {Object} { expr: string, end: number } or null if invalid
|
|
307
|
+
*/
|
|
308
|
+
function parseBalancedExpression(str, start) {
|
|
309
|
+
if (str[start] !== '{') return null;
|
|
310
|
+
|
|
311
|
+
let depth = 0;
|
|
312
|
+
let inString = false;
|
|
313
|
+
let stringChar = '';
|
|
314
|
+
let i = start;
|
|
315
|
+
|
|
316
|
+
while (i < str.length) {
|
|
317
|
+
const char = str[i];
|
|
318
|
+
const prevChar = i > 0 ? str[i - 1] : '';
|
|
319
|
+
|
|
320
|
+
// Handle string literals
|
|
321
|
+
if (!inString && (char === '"' || char === "'" || char === '`')) {
|
|
322
|
+
inString = true;
|
|
323
|
+
stringChar = char;
|
|
324
|
+
} else if (inString && char === stringChar && prevChar !== '\\') {
|
|
325
|
+
inString = false;
|
|
326
|
+
stringChar = '';
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Count braces only outside strings
|
|
330
|
+
if (!inString) {
|
|
331
|
+
if (char === '{') {
|
|
332
|
+
depth++;
|
|
333
|
+
} else if (char === '}') {
|
|
334
|
+
depth--;
|
|
335
|
+
if (depth === 0) {
|
|
336
|
+
// Found the matching closing brace
|
|
337
|
+
return {
|
|
338
|
+
expr: str.slice(start + 1, i),
|
|
339
|
+
end: i
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
i++;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return null; // Unbalanced braces
|
|
349
|
+
}
|
|
350
|
+
|
|
301
351
|
/**
|
|
302
352
|
* Extract dynamic attributes from a selector
|
|
303
353
|
* Returns { cleanSelector, dynamicAttrs } where dynamicAttrs is an array of { name, expr }
|
|
354
|
+
* Handles complex expressions including ternaries, nested braces, and string literals
|
|
304
355
|
* @param {string} selector - CSS selector with potential dynamic attributes
|
|
305
356
|
* @returns {Object} { cleanSelector, dynamicAttrs }
|
|
306
357
|
*/
|
|
307
358
|
function extractDynamicAttributes(selector) {
|
|
308
359
|
const dynamicAttrs = [];
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
360
|
+
let cleanSelector = '';
|
|
361
|
+
let i = 0;
|
|
362
|
+
|
|
363
|
+
while (i < selector.length) {
|
|
364
|
+
// Look for attribute start: [
|
|
365
|
+
if (selector[i] === '[') {
|
|
366
|
+
i++; // Skip [
|
|
367
|
+
|
|
368
|
+
// Parse attribute name
|
|
369
|
+
let attrName = '';
|
|
370
|
+
while (i < selector.length && /[a-zA-Z0-9-]/.test(selector[i])) {
|
|
371
|
+
attrName += selector[i];
|
|
372
|
+
i++;
|
|
373
|
+
}
|
|
312
374
|
|
|
313
|
-
|
|
375
|
+
// Skip whitespace
|
|
376
|
+
while (i < selector.length && /\s/.test(selector[i])) i++;
|
|
314
377
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
378
|
+
// Check for =
|
|
379
|
+
if (i < selector.length && selector[i] === '=') {
|
|
380
|
+
i++; // Skip =
|
|
381
|
+
|
|
382
|
+
// Skip whitespace
|
|
383
|
+
while (i < selector.length && /\s/.test(selector[i])) i++;
|
|
321
384
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
385
|
+
// Check for optional quote
|
|
386
|
+
const hasQuote = selector[i] === '"';
|
|
387
|
+
if (hasQuote) i++;
|
|
388
|
+
|
|
389
|
+
// Check for dynamic expression {
|
|
390
|
+
if (selector[i] === '{') {
|
|
391
|
+
const result = parseBalancedExpression(selector, i);
|
|
392
|
+
if (result) {
|
|
393
|
+
dynamicAttrs.push({ name: attrName, expr: result.expr });
|
|
394
|
+
i = result.end + 1; // Skip past closing }
|
|
395
|
+
|
|
396
|
+
// Skip optional closing quote
|
|
397
|
+
if (hasQuote && selector[i] === '"') i++;
|
|
398
|
+
|
|
399
|
+
// Skip closing ]
|
|
400
|
+
if (selector[i] === ']') i++;
|
|
401
|
+
|
|
402
|
+
// Don't add this attribute to cleanSelector
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Not a dynamic attribute, copy everything from [ to ]
|
|
409
|
+
let bracketDepth = 1;
|
|
410
|
+
cleanSelector += '[';
|
|
411
|
+
while (i < selector.length && bracketDepth > 0) {
|
|
412
|
+
if (selector[i] === '[') bracketDepth++;
|
|
413
|
+
else if (selector[i] === ']') bracketDepth--;
|
|
414
|
+
cleanSelector += selector[i];
|
|
415
|
+
i++;
|
|
416
|
+
}
|
|
417
|
+
} else {
|
|
418
|
+
cleanSelector += selector[i];
|
|
419
|
+
i++;
|
|
420
|
+
}
|
|
325
421
|
}
|
|
326
|
-
cleanSelector = cleanSelector.replace(attrPatternQuoted, '');
|
|
327
422
|
|
|
328
423
|
return { cleanSelector, dynamicAttrs };
|
|
329
424
|
}
|
|
@@ -577,6 +672,18 @@ export function transformComponentCall(transformer, node, indent) {
|
|
|
577
672
|
* @param {number} indent - Indentation level
|
|
578
673
|
* @returns {string} JavaScript code
|
|
579
674
|
*/
|
|
675
|
+
/**
|
|
676
|
+
* Escape a string for use in a template literal
|
|
677
|
+
* @param {string} str - String to escape
|
|
678
|
+
* @returns {string} Escaped string
|
|
679
|
+
*/
|
|
680
|
+
function escapeTemplateString(str) {
|
|
681
|
+
return str
|
|
682
|
+
.replace(/\\/g, '\\\\') // Escape backslashes first
|
|
683
|
+
.replace(/`/g, '\\`') // Escape backticks
|
|
684
|
+
.replace(/\$/g, '\\$'); // Escape dollar signs to prevent ${} interpretation
|
|
685
|
+
}
|
|
686
|
+
|
|
580
687
|
export function transformTextNode(transformer, node, indent) {
|
|
581
688
|
const pad = ' '.repeat(indent);
|
|
582
689
|
const parts = node.parts;
|
|
@@ -589,7 +696,8 @@ export function transformTextNode(transformer, node, indent) {
|
|
|
589
696
|
// Has interpolations - use text() with a function
|
|
590
697
|
const textParts = parts.map(part => {
|
|
591
698
|
if (typeof part === 'string') {
|
|
592
|
-
|
|
699
|
+
// Escape for template literal (not JSON.stringify which adds quotes)
|
|
700
|
+
return escapeTemplateString(part);
|
|
593
701
|
}
|
|
594
702
|
// Interpolation
|
|
595
703
|
const expr = transformExpressionString(transformer, part.expression);
|