esupgrade 2025.1.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/AGENTS.md +3 -0
- package/README.md +48 -0
- package/bin/esupgrade.js +47 -30
- package/package.json +1 -1
- package/src/widelyAvailable.js +477 -16
- package/tests/cli.test.js +13 -17
- package/tests/widelyAvailable.test.js +1108 -0
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
|
|
|
@@ -335,3 +392,407 @@ export function arrayFromToSpread(j, root) {
|
|
|
335
392
|
|
|
336
393
|
return { modified, changes }
|
|
337
394
|
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Transform Math.pow() to exponentiation operator (**)
|
|
398
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Exponentiation
|
|
399
|
+
*/
|
|
400
|
+
export function mathPowToExponentiation(j, root) {
|
|
401
|
+
let modified = false
|
|
402
|
+
const changes = []
|
|
403
|
+
|
|
404
|
+
root
|
|
405
|
+
.find(j.CallExpression, {
|
|
406
|
+
callee: {
|
|
407
|
+
type: "MemberExpression",
|
|
408
|
+
object: { name: "Math" },
|
|
409
|
+
property: { name: "pow" },
|
|
410
|
+
},
|
|
411
|
+
})
|
|
412
|
+
.filter((path) => {
|
|
413
|
+
// Must have exactly 2 arguments (base and exponent)
|
|
414
|
+
return path.node.arguments.length === 2
|
|
415
|
+
})
|
|
416
|
+
.forEach((path) => {
|
|
417
|
+
const node = path.node
|
|
418
|
+
const [base, exponent] = node.arguments
|
|
419
|
+
|
|
420
|
+
// Create exponentiation expression
|
|
421
|
+
const expExpression = j.binaryExpression("**", base, exponent)
|
|
422
|
+
|
|
423
|
+
j(path).replaceWith(expExpression)
|
|
424
|
+
|
|
425
|
+
modified = true
|
|
426
|
+
if (node.loc) {
|
|
427
|
+
changes.push({
|
|
428
|
+
type: "mathPowToExponentiation",
|
|
429
|
+
line: node.loc.start.line,
|
|
430
|
+
})
|
|
431
|
+
}
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
return { modified, changes }
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Transform traditional for loops to for...of where safe
|
|
439
|
+
* Converts: for (let i = 0; i < arr.length; i++) { const item = arr[i]; ... }
|
|
440
|
+
* To: for (const item of arr) { ... }
|
|
441
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
|
|
442
|
+
*/
|
|
443
|
+
export function forLoopToForOf(j, root) {
|
|
444
|
+
let modified = false
|
|
445
|
+
const changes = []
|
|
446
|
+
|
|
447
|
+
root
|
|
448
|
+
.find(j.ForStatement)
|
|
449
|
+
.filter((path) => {
|
|
450
|
+
const node = path.node
|
|
451
|
+
|
|
452
|
+
// Check init: must be let/const i = 0
|
|
453
|
+
if (!j.VariableDeclaration.check(node.init)) {
|
|
454
|
+
return false
|
|
455
|
+
}
|
|
456
|
+
if (node.init.declarations.length !== 1) {
|
|
457
|
+
return false
|
|
458
|
+
}
|
|
459
|
+
const initDeclarator = node.init.declarations[0]
|
|
460
|
+
if (!j.Identifier.check(initDeclarator.id)) {
|
|
461
|
+
return false
|
|
462
|
+
}
|
|
463
|
+
const indexVar = initDeclarator.id.name
|
|
464
|
+
if (!j.Literal.check(initDeclarator.init) || initDeclarator.init.value !== 0) {
|
|
465
|
+
return false
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Check test: must be i < arr.length
|
|
469
|
+
if (!j.BinaryExpression.check(node.test)) {
|
|
470
|
+
return false
|
|
471
|
+
}
|
|
472
|
+
if (node.test.operator !== "<") {
|
|
473
|
+
return false
|
|
474
|
+
}
|
|
475
|
+
if (!j.Identifier.check(node.test.left) || node.test.left.name !== indexVar) {
|
|
476
|
+
return false
|
|
477
|
+
}
|
|
478
|
+
if (!j.MemberExpression.check(node.test.right)) {
|
|
479
|
+
return false
|
|
480
|
+
}
|
|
481
|
+
if (
|
|
482
|
+
!j.Identifier.check(node.test.right.property) ||
|
|
483
|
+
node.test.right.property.name !== "length"
|
|
484
|
+
) {
|
|
485
|
+
return false
|
|
486
|
+
}
|
|
487
|
+
if (!j.Identifier.check(node.test.right.object)) {
|
|
488
|
+
return false
|
|
489
|
+
}
|
|
490
|
+
const arrayVar = node.test.right.object.name
|
|
491
|
+
|
|
492
|
+
// Check update: must be i++ or ++i
|
|
493
|
+
if (j.UpdateExpression.check(node.update)) {
|
|
494
|
+
if (
|
|
495
|
+
!j.Identifier.check(node.update.argument) ||
|
|
496
|
+
node.update.argument.name !== indexVar ||
|
|
497
|
+
node.update.operator !== "++"
|
|
498
|
+
) {
|
|
499
|
+
return false
|
|
500
|
+
}
|
|
501
|
+
} else {
|
|
502
|
+
return false
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Check body: must be a block statement
|
|
506
|
+
if (!j.BlockStatement.check(node.body)) {
|
|
507
|
+
return false
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Look for first statement that assigns arr[i] to a variable
|
|
511
|
+
if (node.body.body.length === 0) {
|
|
512
|
+
return false
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const firstStmt = node.body.body[0]
|
|
516
|
+
if (!j.VariableDeclaration.check(firstStmt)) {
|
|
517
|
+
return false
|
|
518
|
+
}
|
|
519
|
+
if (firstStmt.declarations.length !== 1) {
|
|
520
|
+
return false
|
|
521
|
+
}
|
|
522
|
+
const varDeclarator = firstStmt.declarations[0]
|
|
523
|
+
if (!j.Identifier.check(varDeclarator.id)) {
|
|
524
|
+
return false
|
|
525
|
+
}
|
|
526
|
+
if (!j.MemberExpression.check(varDeclarator.init)) {
|
|
527
|
+
return false
|
|
528
|
+
}
|
|
529
|
+
if (
|
|
530
|
+
!j.Identifier.check(varDeclarator.init.object) ||
|
|
531
|
+
varDeclarator.init.object.name !== arrayVar
|
|
532
|
+
) {
|
|
533
|
+
return false
|
|
534
|
+
}
|
|
535
|
+
if (
|
|
536
|
+
!j.Identifier.check(varDeclarator.init.property) ||
|
|
537
|
+
varDeclarator.init.property.name !== indexVar ||
|
|
538
|
+
varDeclarator.init.computed !== true
|
|
539
|
+
) {
|
|
540
|
+
return false
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Check that the index variable is not used elsewhere in the body
|
|
544
|
+
const bodyWithoutFirst = node.body.body.slice(1)
|
|
545
|
+
let indexVarUsed = false
|
|
546
|
+
|
|
547
|
+
// Recursively check if identifier is used in AST nodes
|
|
548
|
+
const checkNode = (astNode) => {
|
|
549
|
+
if (!astNode || typeof astNode !== "object") return
|
|
550
|
+
|
|
551
|
+
if (astNode.type === "Identifier" && astNode.name === indexVar) {
|
|
552
|
+
indexVarUsed = true
|
|
553
|
+
return
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// Traverse all properties
|
|
557
|
+
for (const key in astNode) {
|
|
558
|
+
if (
|
|
559
|
+
key === "loc" ||
|
|
560
|
+
key === "start" ||
|
|
561
|
+
key === "end" ||
|
|
562
|
+
key === "tokens" ||
|
|
563
|
+
key === "comments"
|
|
564
|
+
)
|
|
565
|
+
continue
|
|
566
|
+
const value = astNode[key]
|
|
567
|
+
if (Array.isArray(value)) {
|
|
568
|
+
value.forEach(checkNode)
|
|
569
|
+
} else if (value && typeof value === "object") {
|
|
570
|
+
checkNode(value)
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
bodyWithoutFirst.forEach(checkNode)
|
|
576
|
+
|
|
577
|
+
if (indexVarUsed) {
|
|
578
|
+
return false
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return true
|
|
582
|
+
})
|
|
583
|
+
.forEach((path) => {
|
|
584
|
+
const node = path.node
|
|
585
|
+
const arrayVar = node.test.right.object.name
|
|
586
|
+
const itemVar = node.body.body[0].declarations[0].id.name
|
|
587
|
+
const itemKind = node.body.body[0].kind
|
|
588
|
+
|
|
589
|
+
// Create new body without the first declaration
|
|
590
|
+
const newBody = j.blockStatement(node.body.body.slice(1))
|
|
591
|
+
|
|
592
|
+
// Create for...of loop
|
|
593
|
+
const forOfLoop = j.forOfStatement(
|
|
594
|
+
j.variableDeclaration(itemKind, [j.variableDeclarator(j.identifier(itemVar))]),
|
|
595
|
+
j.identifier(arrayVar),
|
|
596
|
+
newBody,
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
j(path).replaceWith(forOfLoop)
|
|
600
|
+
|
|
601
|
+
modified = true
|
|
602
|
+
if (node.loc) {
|
|
603
|
+
changes.push({
|
|
604
|
+
type: "forLoopToForOf",
|
|
605
|
+
line: node.loc.start.line,
|
|
606
|
+
})
|
|
607
|
+
}
|
|
608
|
+
})
|
|
609
|
+
|
|
610
|
+
return { modified, changes }
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Transform iterables' forEach() to for...of loop
|
|
615
|
+
* Handles DOM APIs like querySelectorAll, getElementsBy*, etc. and other known iterables
|
|
616
|
+
* Only transforms when forEach callback is declared inline with a function body (block statement)
|
|
617
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
|
|
618
|
+
*/
|
|
619
|
+
export function iterableForEachToForOf(j, root) {
|
|
620
|
+
let modified = false
|
|
621
|
+
const changes = []
|
|
622
|
+
|
|
623
|
+
// Define known iterable-returning methods by their object/context
|
|
624
|
+
const knownIterableMethods = {
|
|
625
|
+
document: [
|
|
626
|
+
"querySelectorAll",
|
|
627
|
+
"getElementsByTagName",
|
|
628
|
+
"getElementsByClassName",
|
|
629
|
+
"getElementsByName",
|
|
630
|
+
],
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Define known iterable properties
|
|
634
|
+
const knownIterableProperties = {
|
|
635
|
+
window: ["frames"],
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
root
|
|
639
|
+
.find(j.CallExpression)
|
|
640
|
+
.filter((path) => {
|
|
641
|
+
const node = path.node
|
|
642
|
+
// Check if this is a forEach call
|
|
643
|
+
if (
|
|
644
|
+
!j.MemberExpression.check(node.callee) ||
|
|
645
|
+
!j.Identifier.check(node.callee.property) ||
|
|
646
|
+
node.callee.property.name !== "forEach"
|
|
647
|
+
) {
|
|
648
|
+
return false
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const object = node.callee.object
|
|
652
|
+
|
|
653
|
+
// Check if this is a property access pattern like window.frames
|
|
654
|
+
if (j.MemberExpression.check(object) && !j.CallExpression.check(object)) {
|
|
655
|
+
const objectName = j.Identifier.check(object.object) ? object.object.name : null
|
|
656
|
+
const propertyName = j.Identifier.check(object.property)
|
|
657
|
+
? object.property.name
|
|
658
|
+
: null
|
|
659
|
+
|
|
660
|
+
if (
|
|
661
|
+
objectName &&
|
|
662
|
+
propertyName &&
|
|
663
|
+
knownIterableProperties[objectName] &&
|
|
664
|
+
knownIterableProperties[objectName].includes(propertyName)
|
|
665
|
+
) {
|
|
666
|
+
// This is a valid iterable property like window.frames - continue to callback check
|
|
667
|
+
} else {
|
|
668
|
+
// Not a known iterable property
|
|
669
|
+
return false
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
// Check for method call patterns like document.querySelectorAll()
|
|
673
|
+
else if (j.CallExpression.check(object)) {
|
|
674
|
+
// Check if it's a member expression (e.g., document.querySelectorAll)
|
|
675
|
+
if (!j.MemberExpression.check(object.callee)) {
|
|
676
|
+
return false
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Get the method name
|
|
680
|
+
const methodName = j.Identifier.check(object.callee.property)
|
|
681
|
+
? object.callee.property.name
|
|
682
|
+
: null
|
|
683
|
+
|
|
684
|
+
if (!methodName) {
|
|
685
|
+
return false
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// Verify the object is document only
|
|
689
|
+
const callerObject = object.callee.object
|
|
690
|
+
if (j.Identifier.check(callerObject)) {
|
|
691
|
+
const objectName = callerObject.name
|
|
692
|
+
// Only allow document
|
|
693
|
+
if (objectName !== "document") {
|
|
694
|
+
return false
|
|
695
|
+
}
|
|
696
|
+
// Verify method belongs to document
|
|
697
|
+
if (!knownIterableMethods.document.includes(methodName)) {
|
|
698
|
+
return false
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
// Handle cases like document.getElementById('x').querySelectorAll()
|
|
702
|
+
// Only allow these from document-originating chains
|
|
703
|
+
else if (
|
|
704
|
+
j.MemberExpression.check(callerObject) ||
|
|
705
|
+
j.CallExpression.check(callerObject)
|
|
706
|
+
) {
|
|
707
|
+
// Check if this eventually chains from document
|
|
708
|
+
const isFromDocument = (node) => {
|
|
709
|
+
if (j.Identifier.check(node)) {
|
|
710
|
+
return node.name === "document"
|
|
711
|
+
}
|
|
712
|
+
if (j.MemberExpression.check(node)) {
|
|
713
|
+
return isFromDocument(node.object)
|
|
714
|
+
}
|
|
715
|
+
if (j.CallExpression.check(node)) {
|
|
716
|
+
if (j.MemberExpression.check(node.callee)) {
|
|
717
|
+
return isFromDocument(node.callee.object)
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return false
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
if (!isFromDocument(callerObject)) {
|
|
724
|
+
return false
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// Verify the method is valid for document
|
|
728
|
+
if (!knownIterableMethods.document.includes(methodName)) {
|
|
729
|
+
return false
|
|
730
|
+
}
|
|
731
|
+
} else {
|
|
732
|
+
return false
|
|
733
|
+
}
|
|
734
|
+
} else {
|
|
735
|
+
return false
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// Check that forEach has a callback argument
|
|
739
|
+
if (node.arguments.length === 0) {
|
|
740
|
+
return false
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
const callback = node.arguments[0]
|
|
744
|
+
// Only transform if callback is an inline function (arrow or function expression)
|
|
745
|
+
if (
|
|
746
|
+
!j.ArrowFunctionExpression.check(callback) &&
|
|
747
|
+
!j.FunctionExpression.check(callback)
|
|
748
|
+
) {
|
|
749
|
+
return false
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// Only transform if the callback has a block statement body (with braces)
|
|
753
|
+
// Arrow functions with expression bodies (e.g., item => item.value) should NOT be transformed
|
|
754
|
+
if (!j.BlockStatement.check(callback.body)) {
|
|
755
|
+
return false
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// Only transform if callback uses only the first parameter (element)
|
|
759
|
+
// Don't transform if it uses index or array parameters
|
|
760
|
+
const params = callback.params
|
|
761
|
+
if (params.length !== 1) {
|
|
762
|
+
return false
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
return true
|
|
766
|
+
})
|
|
767
|
+
.forEach((path) => {
|
|
768
|
+
const node = path.node
|
|
769
|
+
const iterable = node.callee.object
|
|
770
|
+
const callback = node.arguments[0]
|
|
771
|
+
|
|
772
|
+
const itemParam = callback.params[0]
|
|
773
|
+
const body = callback.body
|
|
774
|
+
|
|
775
|
+
// Create for...of loop
|
|
776
|
+
const forOfLoop = j.forOfStatement(
|
|
777
|
+
j.variableDeclaration("const", [j.variableDeclarator(itemParam)]),
|
|
778
|
+
iterable,
|
|
779
|
+
body,
|
|
780
|
+
)
|
|
781
|
+
|
|
782
|
+
// Replace the expression statement containing the forEach call
|
|
783
|
+
const statement = path.parent
|
|
784
|
+
if (j.ExpressionStatement.check(statement.node)) {
|
|
785
|
+
j(statement).replaceWith(forOfLoop)
|
|
786
|
+
|
|
787
|
+
modified = true
|
|
788
|
+
if (node.loc) {
|
|
789
|
+
changes.push({
|
|
790
|
+
type: "iterableForEachToForOf",
|
|
791
|
+
line: node.loc.start.line,
|
|
792
|
+
})
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
})
|
|
796
|
+
|
|
797
|
+
return { modified, changes }
|
|
798
|
+
}
|
package/tests/cli.test.js
CHANGED
|
@@ -41,7 +41,7 @@ describe("CLI", () => {
|
|
|
41
41
|
|
|
42
42
|
const transformedCode = fs.readFileSync(testFile, "utf8")
|
|
43
43
|
assert.match(transformedCode, /const x = 1/)
|
|
44
|
-
assert.match(result.stdout,
|
|
44
|
+
assert.match(result.stdout, /✓ 1 file\(s\) upgraded/)
|
|
45
45
|
assert.strictEqual(result.status, 0)
|
|
46
46
|
})
|
|
47
47
|
|
|
@@ -57,7 +57,6 @@ describe("CLI", () => {
|
|
|
57
57
|
const transformedCode = fs.readFileSync(testFile, "utf8")
|
|
58
58
|
assert.match(transformedCode, /const x = 1/)
|
|
59
59
|
assert.doesNotMatch(transformedCode, /Promise\.try/) // Promise.try not in widely-available
|
|
60
|
-
assert.match(result.stdout, /widely-available/)
|
|
61
60
|
assert.strictEqual(result.status, 0)
|
|
62
61
|
})
|
|
63
62
|
|
|
@@ -77,7 +76,6 @@ describe("CLI", () => {
|
|
|
77
76
|
const transformedCode = fs.readFileSync(testFile, "utf8")
|
|
78
77
|
assert.match(transformedCode, /const x = 1/)
|
|
79
78
|
assert.match(transformedCode, /Promise\.try/) // Promise.try in newly-available
|
|
80
|
-
assert.match(result.stdout, /newly-available/)
|
|
81
79
|
assert.strictEqual(result.status, 0)
|
|
82
80
|
})
|
|
83
81
|
|
|
@@ -108,7 +106,7 @@ describe("CLI", () => {
|
|
|
108
106
|
|
|
109
107
|
const fileContent = fs.readFileSync(testFile, "utf8")
|
|
110
108
|
assert.strictEqual(fileContent, originalCode) // File unchanged
|
|
111
|
-
assert.match(result.stdout, /All files are
|
|
109
|
+
assert.match(result.stdout, /All files are up to date/)
|
|
112
110
|
assert.strictEqual(result.status, 0)
|
|
113
111
|
})
|
|
114
112
|
|
|
@@ -128,8 +126,7 @@ describe("CLI", () => {
|
|
|
128
126
|
const transformedCode = fs.readFileSync(testFile, "utf8")
|
|
129
127
|
assert.match(transformedCode, /const x = 1/)
|
|
130
128
|
assert.match(result.stdout, /✗/)
|
|
131
|
-
assert.match(result.stdout, /Changes
|
|
132
|
-
assert.match(result.stdout, /1 file\(s\) upgraded/)
|
|
129
|
+
assert.match(result.stdout, /Changes have been written/)
|
|
133
130
|
assert.strictEqual(result.status, 1) // Still exit 1 with --check
|
|
134
131
|
})
|
|
135
132
|
|
|
@@ -150,7 +147,6 @@ describe("CLI", () => {
|
|
|
150
147
|
const transformed2 = fs.readFileSync(file2, "utf8")
|
|
151
148
|
assert.match(transformed1, /const x = 1/)
|
|
152
149
|
assert.match(transformed2, /const y = 2/)
|
|
153
|
-
assert.match(result.stdout, /Processing 2 file/)
|
|
154
150
|
assert.match(result.stdout, /2 file\(s\) upgraded/)
|
|
155
151
|
assert.strictEqual(result.status, 0)
|
|
156
152
|
})
|
|
@@ -172,7 +168,7 @@ describe("CLI", () => {
|
|
|
172
168
|
encoding: "utf8",
|
|
173
169
|
})
|
|
174
170
|
|
|
175
|
-
|
|
171
|
+
// Should only process file1 in tempDir, not in node_modules or .git
|
|
176
172
|
assert.strictEqual(result.status, 0)
|
|
177
173
|
})
|
|
178
174
|
|
|
@@ -191,7 +187,6 @@ describe("CLI", () => {
|
|
|
191
187
|
encoding: "utf8",
|
|
192
188
|
})
|
|
193
189
|
|
|
194
|
-
assert.match(result.stdout, /Processing 4 file/)
|
|
195
190
|
assert.match(result.stdout, /4 file\(s\) upgraded/)
|
|
196
191
|
assert.strictEqual(result.status, 0)
|
|
197
192
|
})
|
|
@@ -207,7 +202,6 @@ describe("CLI", () => {
|
|
|
207
202
|
encoding: "utf8",
|
|
208
203
|
})
|
|
209
204
|
|
|
210
|
-
assert.match(result.stdout, /Processing 2 file/)
|
|
211
205
|
assert.strictEqual(result.status, 0)
|
|
212
206
|
})
|
|
213
207
|
|
|
@@ -250,7 +244,6 @@ describe("CLI", () => {
|
|
|
250
244
|
})
|
|
251
245
|
|
|
252
246
|
assert.match(result.stdout, /var to const/)
|
|
253
|
-
assert.match(result.stdout, /line/)
|
|
254
247
|
assert.strictEqual(result.status, 1)
|
|
255
248
|
})
|
|
256
249
|
|
|
@@ -268,7 +261,7 @@ describe("CLI", () => {
|
|
|
268
261
|
const transformed2 = fs.readFileSync(file2, "utf8")
|
|
269
262
|
assert.match(transformed1, /const x = 1/)
|
|
270
263
|
assert.match(transformed2, /const y = 2/)
|
|
271
|
-
assert.match(result.stdout, /
|
|
264
|
+
assert.match(result.stdout, /2 file\(s\) upgraded/)
|
|
272
265
|
assert.strictEqual(result.status, 0)
|
|
273
266
|
})
|
|
274
267
|
|
|
@@ -283,7 +276,7 @@ describe("CLI", () => {
|
|
|
283
276
|
|
|
284
277
|
const fileContent = fs.readFileSync(testFile, "utf8")
|
|
285
278
|
assert.strictEqual(fileContent, originalCode)
|
|
286
|
-
assert.match(result.stdout, /All files are
|
|
279
|
+
assert.match(result.stdout, /All files are up to date/)
|
|
287
280
|
assert.strictEqual(result.status, 0)
|
|
288
281
|
})
|
|
289
282
|
|
|
@@ -296,7 +289,7 @@ describe("CLI", () => {
|
|
|
296
289
|
encoding: "utf8",
|
|
297
290
|
})
|
|
298
291
|
|
|
299
|
-
assert.match(result.stdout, /
|
|
292
|
+
assert.match(result.stdout, /All files are up to date/)
|
|
300
293
|
assert.strictEqual(result.status, 0)
|
|
301
294
|
})
|
|
302
295
|
|
|
@@ -322,7 +315,6 @@ describe("CLI", () => {
|
|
|
322
315
|
})
|
|
323
316
|
|
|
324
317
|
assert.match(result.stdout, /var to const/)
|
|
325
|
-
assert.match(result.stdout, /lines:/)
|
|
326
318
|
assert.strictEqual(result.status, 1)
|
|
327
319
|
})
|
|
328
320
|
|
|
@@ -335,7 +327,7 @@ describe("CLI", () => {
|
|
|
335
327
|
encoding: "utf8",
|
|
336
328
|
})
|
|
337
329
|
|
|
338
|
-
assert.match(result.stderr,
|
|
330
|
+
assert.match(result.stderr, /✗ Error:/)
|
|
339
331
|
assert.strictEqual(result.status, 0) // CLI continues despite errors
|
|
340
332
|
})
|
|
341
333
|
|
|
@@ -352,7 +344,11 @@ describe("CLI", () => {
|
|
|
352
344
|
encoding: "utf8",
|
|
353
345
|
})
|
|
354
346
|
|
|
355
|
-
|
|
347
|
+
const file1Content = fs.readFileSync(file1, "utf8")
|
|
348
|
+
const file2Content = fs.readFileSync(file2, "utf8")
|
|
349
|
+
assert.match(file1Content, /const x = 1/)
|
|
350
|
+
assert.match(file2Content, /const y = 2/)
|
|
351
|
+
assert.match(result.stdout, /2 file\(s\) upgraded/)
|
|
356
352
|
assert.strictEqual(result.status, 0)
|
|
357
353
|
})
|
|
358
354
|
})
|