esupgrade 2025.2.0 → 2025.2.1
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/package.json +1 -1
- package/src/widelyAvailable.js +73 -16
- package/tests/widelyAvailable.test.js +77 -0
package/package.json
CHANGED
package/src/widelyAvailable.js
CHANGED
|
@@ -51,26 +51,83 @@ export function concatToTemplateLiteral(j, root) {
|
|
|
51
51
|
const parts = []
|
|
52
52
|
const expressions = []
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
flatten(node.right)
|
|
58
|
-
} else if (
|
|
54
|
+
// Helper to check if a node is a string literal
|
|
55
|
+
const isStringLiteral = (node) => {
|
|
56
|
+
return (
|
|
59
57
|
j.StringLiteral.check(node) ||
|
|
60
58
|
(j.Literal.check(node) && typeof node.value === "string")
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Helper to check if a node contains any string literal
|
|
63
|
+
const containsStringLiteral = (node) => {
|
|
64
|
+
if (isStringLiteral(node)) return true
|
|
65
|
+
if (j.BinaryExpression.check(node) && node.operator === "+") {
|
|
66
|
+
return containsStringLiteral(node.left) || containsStringLiteral(node.right)
|
|
67
|
+
}
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const addStringPart = (value) => {
|
|
72
|
+
if (parts.length === 0 || expressions.length >= parts.length) {
|
|
73
|
+
parts.push(value)
|
|
68
74
|
} else {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
parts[parts.length - 1] += value
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const addExpression = (expr) => {
|
|
80
|
+
if (parts.length === 0) {
|
|
81
|
+
parts.push("")
|
|
82
|
+
}
|
|
83
|
+
expressions.push(expr)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const flatten = (node, stringContext = false) => {
|
|
87
|
+
// Note: node is always a BinaryExpression when called, as non-BinaryExpression
|
|
88
|
+
// nodes are handled inline before recursing into flatten
|
|
89
|
+
if (j.BinaryExpression.check(node) && node.operator === "+") {
|
|
90
|
+
// Check if this entire binary expression contains any string literal
|
|
91
|
+
const hasString = containsStringLiteral(node)
|
|
92
|
+
|
|
93
|
+
if (!hasString && !stringContext) {
|
|
94
|
+
// This is pure numeric addition (no strings anywhere), keep as expression
|
|
95
|
+
addExpression(node)
|
|
96
|
+
} else {
|
|
97
|
+
// This binary expression is part of string concatenation
|
|
98
|
+
// Check each operand
|
|
99
|
+
const leftHasString = containsStringLiteral(node.left)
|
|
100
|
+
|
|
101
|
+
// Process left side
|
|
102
|
+
if (j.BinaryExpression.check(node.left) && node.left.operator === "+") {
|
|
103
|
+
// Left is also a + expression - recurse
|
|
104
|
+
flatten(node.left, stringContext)
|
|
105
|
+
} else if (isStringLiteral(node.left)) {
|
|
106
|
+
// Left is a string literal
|
|
107
|
+
addStringPart(node.left.value)
|
|
108
|
+
} else {
|
|
109
|
+
// Left is some other expression
|
|
110
|
+
addExpression(node.left)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Process right side - it's in string context if left had a string
|
|
114
|
+
const rightInStringContext = stringContext || leftHasString
|
|
115
|
+
if (j.BinaryExpression.check(node.right) && node.right.operator === "+") {
|
|
116
|
+
// If right is a + expression with no strings and we're in string context, keep it as a unit
|
|
117
|
+
if (!containsStringLiteral(node.right) && rightInStringContext) {
|
|
118
|
+
addExpression(node.right)
|
|
119
|
+
} else {
|
|
120
|
+
// Right has strings or we need to flatten it
|
|
121
|
+
flatten(node.right, rightInStringContext)
|
|
122
|
+
}
|
|
123
|
+
} else if (isStringLiteral(node.right)) {
|
|
124
|
+
// Right is a string literal
|
|
125
|
+
addStringPart(node.right.value)
|
|
126
|
+
} else {
|
|
127
|
+
// Right is some other expression
|
|
128
|
+
addExpression(node.right)
|
|
129
|
+
}
|
|
72
130
|
}
|
|
73
|
-
expressions.push(node)
|
|
74
131
|
}
|
|
75
132
|
}
|
|
76
133
|
|
|
@@ -480,6 +480,83 @@ var x = 1;`
|
|
|
480
480
|
assert.strictEqual(result.modified, true)
|
|
481
481
|
assert.match(result.code, /`/)
|
|
482
482
|
})
|
|
483
|
+
|
|
484
|
+
test("numeric addition followed by string concatenation", () => {
|
|
485
|
+
const input = `cal_box.style.left = findPosX(cal_link) + 17 + 'px';`
|
|
486
|
+
|
|
487
|
+
const result = transform(input)
|
|
488
|
+
|
|
489
|
+
assert.strictEqual(result.modified, true)
|
|
490
|
+
// Should treat (findPosX(cal_link) + 17) as a single numeric expression
|
|
491
|
+
assert.match(result.code, /`\$\{findPosX\(cal_link\) \+ 17\}px`/)
|
|
492
|
+
})
|
|
493
|
+
|
|
494
|
+
test("multiple numeric additions followed by string concatenation", () => {
|
|
495
|
+
const input = `const result = a + b + c + 'd';`
|
|
496
|
+
|
|
497
|
+
const result = transform(input)
|
|
498
|
+
|
|
499
|
+
assert.strictEqual(result.modified, true)
|
|
500
|
+
// Should treat (a + b + c) as a single numeric expression
|
|
501
|
+
assert.match(result.code, /`\$\{a \+ b \+ c\}d`/)
|
|
502
|
+
})
|
|
503
|
+
|
|
504
|
+
test("string concatenation followed by numeric addition", () => {
|
|
505
|
+
const input = `const result = 'Value: ' + x + y;`
|
|
506
|
+
|
|
507
|
+
const result = transform(input)
|
|
508
|
+
|
|
509
|
+
assert.strictEqual(result.modified, true)
|
|
510
|
+
// After first string, all subsequent + are string concatenation
|
|
511
|
+
assert.match(result.code, /`Value: \$\{x\}\$\{y\}`/)
|
|
512
|
+
})
|
|
513
|
+
|
|
514
|
+
test("numeric addition in middle of string concatenations", () => {
|
|
515
|
+
const input = `const result = 'start' + (a + b) + 'end';`
|
|
516
|
+
|
|
517
|
+
const result = transform(input)
|
|
518
|
+
|
|
519
|
+
assert.strictEqual(result.modified, true)
|
|
520
|
+
// Parenthesized numeric expression should be preserved
|
|
521
|
+
// jscodeshift may add parentheses around binary expressions in template literals
|
|
522
|
+
assert.match(result.code, /`start\$\{(\()?a \+ b(\))?\}end`/)
|
|
523
|
+
})
|
|
524
|
+
|
|
525
|
+
test("consecutive string literals should be merged", () => {
|
|
526
|
+
const input = `const msg = 'Hello' + ' ' + 'world';`
|
|
527
|
+
|
|
528
|
+
const result = transform(input)
|
|
529
|
+
|
|
530
|
+
assert.strictEqual(result.modified, true)
|
|
531
|
+
assert.match(result.code, /`Hello world`/)
|
|
532
|
+
})
|
|
533
|
+
|
|
534
|
+
test("string literal followed by non-binary expression", () => {
|
|
535
|
+
const input = `const msg = 'Value: ' + getValue();`
|
|
536
|
+
|
|
537
|
+
const result = transform(input)
|
|
538
|
+
|
|
539
|
+
assert.strictEqual(result.modified, true)
|
|
540
|
+
assert.match(result.code, /`Value: \$\{getValue\(\)\}`/)
|
|
541
|
+
})
|
|
542
|
+
|
|
543
|
+
test("expression followed by string literal", () => {
|
|
544
|
+
const input = `const msg = getValue() + ' is the value';`
|
|
545
|
+
|
|
546
|
+
const result = transform(input)
|
|
547
|
+
|
|
548
|
+
assert.strictEqual(result.modified, true)
|
|
549
|
+
assert.match(result.code, /`\$\{getValue\(\)\} is the value`/)
|
|
550
|
+
})
|
|
551
|
+
|
|
552
|
+
test("non-binary expression in the middle", () => {
|
|
553
|
+
const input = `const msg = 'start' + getValue() + 'end';`
|
|
554
|
+
|
|
555
|
+
const result = transform(input)
|
|
556
|
+
|
|
557
|
+
assert.strictEqual(result.modified, true)
|
|
558
|
+
assert.match(result.code, /`start\$\{getValue\(\)\}end`/)
|
|
559
|
+
})
|
|
483
560
|
})
|
|
484
561
|
|
|
485
562
|
describe("object spread", () => {
|