esupgrade 2025.0.0 → 2025.0.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/.pre-commit-config.yaml +7 -6
- package/README.md +57 -48
- package/bin/esupgrade.js +7 -21
- package/images/logo-dark.svg +3 -4
- package/images/logo-light.svg +1 -2
- package/package.json +1 -1
- package/src/index.js +18 -689
- package/src/newlyAvailable.js +154 -0
- package/src/widelyAvailable.js +265 -0
- package/tests/cli.test.js +358 -0
- package/tests/index.test.js +274 -0
- package/tests/newlyAvailable.test.js +221 -0
- package/tests/widelyAvailable.test.js +428 -0
- package/.idea/copilot.data.migration.ask2agent.xml +0 -6
- package/.idea/esupgrade.iml +0 -8
- package/.idea/inspectionProfiles/Project_Default.xml +0 -28
- package/.idea/inspectionProfiles/profiles_settings.xml +0 -6
- package/.idea/misc.xml +0 -7
- package/.idea/modules.xml +0 -8
- package/.idea/vcs.xml +0 -7
- package/tests/transform.test.js +0 -322
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { describe, test } from "node:test"
|
|
2
|
+
import assert from "node:assert"
|
|
3
|
+
import { transform } from "../src/index.js"
|
|
4
|
+
|
|
5
|
+
describe("newly-available", () => {
|
|
6
|
+
describe("Promise.try", () => {
|
|
7
|
+
test("Promise.try transformation - newly-available", () => {
|
|
8
|
+
const input = `const p = new Promise((resolve) => resolve(getData()));`
|
|
9
|
+
|
|
10
|
+
const result = transform(input, "newly-available")
|
|
11
|
+
|
|
12
|
+
assert.strictEqual(result.modified, true)
|
|
13
|
+
assert.match(result.code, /Promise\.try/)
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
test("Promise.try not in widely-available", () => {
|
|
17
|
+
const input = `const p = new Promise((resolve) => resolve(getData()));`
|
|
18
|
+
|
|
19
|
+
const result = transform(input, "widely-available")
|
|
20
|
+
|
|
21
|
+
// Should not transform Promise with widely-available baseline
|
|
22
|
+
assert.doesNotMatch(result.code, /Promise\.try/)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test("Promise.try with function passed to resolve", () => {
|
|
26
|
+
const input = `const p = new Promise((resolve) => setTimeout(resolve));`
|
|
27
|
+
|
|
28
|
+
const result = transform(input, "newly-available")
|
|
29
|
+
|
|
30
|
+
assert.strictEqual(result.modified, true)
|
|
31
|
+
// Should transform to Promise.try(setTimeout) not Promise.try(() => setTimeout(resolve))
|
|
32
|
+
assert.match(result.code, /Promise\.try\(setTimeout\)/)
|
|
33
|
+
assert.doesNotMatch(result.code, /resolve/)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test("Promise.try should not transform when awaited", () => {
|
|
37
|
+
const input = `async function foo() {
|
|
38
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
39
|
+
}`
|
|
40
|
+
|
|
41
|
+
const result = transform(input, "newly-available")
|
|
42
|
+
|
|
43
|
+
// Should NOT transform awaited Promises
|
|
44
|
+
assert.strictEqual(result.modified, false)
|
|
45
|
+
assert.match(result.code, /await new Promise/)
|
|
46
|
+
assert.doesNotMatch(result.code, /Promise\.try/)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test("Promise.try should not transform non-Promise constructors", () => {
|
|
50
|
+
const input = `const p = new MyPromise((resolve) => resolve(getData()));`
|
|
51
|
+
|
|
52
|
+
const result = transform(input, "newly-available")
|
|
53
|
+
|
|
54
|
+
assert.strictEqual(result.modified, false)
|
|
55
|
+
assert.match(result.code, /new MyPromise/)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test("Promise.try should not transform with 0 arguments", () => {
|
|
59
|
+
const input = `const p = new Promise();`
|
|
60
|
+
|
|
61
|
+
const result = transform(input, "newly-available")
|
|
62
|
+
|
|
63
|
+
assert.strictEqual(result.modified, false)
|
|
64
|
+
assert.match(result.code, /new Promise\(\)/)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test("Promise.try should not transform with multiple arguments", () => {
|
|
68
|
+
const input = `const p = new Promise((resolve) => resolve(1), extraArg);`
|
|
69
|
+
|
|
70
|
+
const result = transform(input, "newly-available")
|
|
71
|
+
|
|
72
|
+
assert.strictEqual(result.modified, false)
|
|
73
|
+
assert.match(result.code, /new Promise/)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test("Promise.try should not transform with non-function argument", () => {
|
|
77
|
+
const input = `const p = new Promise(executor);`
|
|
78
|
+
|
|
79
|
+
const result = transform(input, "newly-available")
|
|
80
|
+
|
|
81
|
+
assert.strictEqual(result.modified, false)
|
|
82
|
+
assert.match(result.code, /new Promise\(executor\)/)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test("Promise.try should not transform with 0 params", () => {
|
|
86
|
+
const input = `const p = new Promise(() => console.log('test'));`
|
|
87
|
+
|
|
88
|
+
const result = transform(input, "newly-available")
|
|
89
|
+
|
|
90
|
+
assert.strictEqual(result.modified, false)
|
|
91
|
+
assert.match(result.code, /new Promise/)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test("Promise.try should not transform with more than 2 params", () => {
|
|
95
|
+
const input = `const p = new Promise((resolve, reject, extra) => resolve(1));`
|
|
96
|
+
|
|
97
|
+
const result = transform(input, "newly-available")
|
|
98
|
+
|
|
99
|
+
assert.strictEqual(result.modified, false)
|
|
100
|
+
assert.match(result.code, /new Promise/)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test("Promise.try with block statement and resolve call", () => {
|
|
104
|
+
const input = `const p = new Promise((resolve) => { resolve(getData()); });`
|
|
105
|
+
|
|
106
|
+
const result = transform(input, "newly-available")
|
|
107
|
+
|
|
108
|
+
assert.strictEqual(result.modified, true)
|
|
109
|
+
assert.match(result.code, /Promise\.try/)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
test("Promise.try with arrow function expression body as function call", () => {
|
|
113
|
+
const input = `const p = new Promise((resolve) => computeValue());`
|
|
114
|
+
|
|
115
|
+
const result = transform(input, "newly-available")
|
|
116
|
+
|
|
117
|
+
// This should not transform because computeValue() is not calling resolve
|
|
118
|
+
assert.strictEqual(result.modified, false)
|
|
119
|
+
assert.match(result.code, /new Promise/)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
test("Promise.try with arrow function returning a value directly", () => {
|
|
123
|
+
const input = `const p = new Promise((resolve) => someFunction(arg1, arg2));`
|
|
124
|
+
|
|
125
|
+
const result = transform(input, "newly-available")
|
|
126
|
+
|
|
127
|
+
// This should not transform - function call that doesn't involve resolve
|
|
128
|
+
assert.strictEqual(result.modified, false)
|
|
129
|
+
assert.match(result.code, /new Promise/)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test("Promise.try should not transform non-call expression body", () => {
|
|
133
|
+
const input = `const p = new Promise((resolve) => someValue);`
|
|
134
|
+
|
|
135
|
+
const result = transform(input, "newly-available")
|
|
136
|
+
|
|
137
|
+
assert.strictEqual(result.modified, false)
|
|
138
|
+
assert.match(result.code, /new Promise/)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
test("Promise.try should not transform call with wrong number of arguments to resolve", () => {
|
|
142
|
+
const input = `const p = new Promise((resolve) => func(resolve, extra));`
|
|
143
|
+
|
|
144
|
+
const result = transform(input, "newly-available")
|
|
145
|
+
|
|
146
|
+
assert.strictEqual(result.modified, false)
|
|
147
|
+
assert.match(result.code, /new Promise/)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
test("Promise.try should not transform call with non-identifier resolve", () => {
|
|
151
|
+
const input = `const p = new Promise((resolve) => func(123));`
|
|
152
|
+
|
|
153
|
+
const result = transform(input, "newly-available")
|
|
154
|
+
|
|
155
|
+
assert.strictEqual(result.modified, false)
|
|
156
|
+
assert.match(result.code, /new Promise/)
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
test("Promise.try should not transform resolve call with 0 arguments", () => {
|
|
160
|
+
const input = `const p = new Promise((resolve) => resolve());`
|
|
161
|
+
|
|
162
|
+
const result = transform(input, "newly-available")
|
|
163
|
+
|
|
164
|
+
assert.strictEqual(result.modified, false)
|
|
165
|
+
assert.match(result.code, /new Promise/)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
test("Promise.try should not transform block with multiple statements", () => {
|
|
169
|
+
const input = `const p = new Promise((resolve) => {
|
|
170
|
+
const data = getData();
|
|
171
|
+
resolve(data);
|
|
172
|
+
});`
|
|
173
|
+
|
|
174
|
+
const result = transform(input, "newly-available")
|
|
175
|
+
|
|
176
|
+
assert.strictEqual(result.modified, false)
|
|
177
|
+
assert.match(result.code, /new Promise/)
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
test("Promise.try should not transform block with non-expression statement", () => {
|
|
181
|
+
const input = `const p = new Promise((resolve) => {
|
|
182
|
+
if (true) resolve(1);
|
|
183
|
+
});`
|
|
184
|
+
|
|
185
|
+
const result = transform(input, "newly-available")
|
|
186
|
+
|
|
187
|
+
assert.strictEqual(result.modified, false)
|
|
188
|
+
assert.match(result.code, /new Promise/)
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
test("Promise.try with function expression", () => {
|
|
192
|
+
const input = `const p = new Promise(function(resolve) { resolve(getData()); });`
|
|
193
|
+
|
|
194
|
+
const result = transform(input, "newly-available")
|
|
195
|
+
|
|
196
|
+
assert.strictEqual(result.modified, true)
|
|
197
|
+
assert.match(result.code, /Promise\.try/)
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
test("Promise.try with both resolve and reject params", () => {
|
|
201
|
+
const input = `const p = new Promise((resolve, reject) => resolve(getData()));`
|
|
202
|
+
|
|
203
|
+
const result = transform(input, "newly-available")
|
|
204
|
+
|
|
205
|
+
assert.strictEqual(result.modified, true)
|
|
206
|
+
assert.match(result.code, /Promise\.try/)
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
test("Promise.try tracks line numbers correctly", () => {
|
|
210
|
+
const input = `// Line 1
|
|
211
|
+
const p = new Promise((resolve) => resolve(getData()));`
|
|
212
|
+
|
|
213
|
+
const result = transform(input, "newly-available")
|
|
214
|
+
|
|
215
|
+
assert.strictEqual(result.modified, true)
|
|
216
|
+
assert.strictEqual(result.changes.length, 1)
|
|
217
|
+
assert.strictEqual(result.changes[0].type, "promiseTry")
|
|
218
|
+
assert.strictEqual(result.changes[0].line, 2)
|
|
219
|
+
})
|
|
220
|
+
})
|
|
221
|
+
})
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import { describe, test } from "node:test"
|
|
2
|
+
import assert from "node:assert"
|
|
3
|
+
import { transform } from "../src/index.js"
|
|
4
|
+
|
|
5
|
+
describe("widely-available", () => {
|
|
6
|
+
describe("for...of loop", () => {
|
|
7
|
+
test("Array.from().forEach() to for...of", () => {
|
|
8
|
+
const input = `
|
|
9
|
+
Array.from(items).forEach(item => {
|
|
10
|
+
console.log(item);
|
|
11
|
+
});
|
|
12
|
+
`
|
|
13
|
+
|
|
14
|
+
const result = transform(input)
|
|
15
|
+
|
|
16
|
+
assert.strictEqual(result.modified, true)
|
|
17
|
+
assert.match(result.code, /for \(const item of items\)/)
|
|
18
|
+
assert.match(result.code, /console\.log\(item\)/)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test("Array.from().forEach() with arrow function expression", () => {
|
|
22
|
+
const input = `Array.from(numbers).forEach(n => console.log(n));`
|
|
23
|
+
|
|
24
|
+
const result = transform(input)
|
|
25
|
+
|
|
26
|
+
assert.strictEqual(result.modified, true)
|
|
27
|
+
assert.match(result.code, /for \(const n of numbers\)/)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test("forEach should NOT transform plain identifiers (cannot confirm iterable)", () => {
|
|
31
|
+
const input = `
|
|
32
|
+
items.forEach(item => {
|
|
33
|
+
console.log(item);
|
|
34
|
+
});
|
|
35
|
+
`
|
|
36
|
+
|
|
37
|
+
const result = transform(input)
|
|
38
|
+
|
|
39
|
+
// Should not transform because we can't statically confirm 'items' is iterable
|
|
40
|
+
// It could be a jscodeshift Collection or other object with forEach but not Symbol.iterator
|
|
41
|
+
assert.strictEqual(result.modified, false)
|
|
42
|
+
assert.match(result.code, /items\.forEach/)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test("forEach should NOT transform plain identifiers with function expression", () => {
|
|
46
|
+
const input = `numbers.forEach(function(n) { console.log(n); });`
|
|
47
|
+
|
|
48
|
+
const result = transform(input)
|
|
49
|
+
|
|
50
|
+
// Should not transform because we can't statically confirm 'numbers' is iterable
|
|
51
|
+
assert.strictEqual(result.modified, false)
|
|
52
|
+
assert.match(result.code, /numbers\.forEach/)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test("for...of Object.keys() to for...in", () => {
|
|
56
|
+
const input = `
|
|
57
|
+
for (const key of Object.keys(obj)) {
|
|
58
|
+
console.log(key);
|
|
59
|
+
}
|
|
60
|
+
`
|
|
61
|
+
|
|
62
|
+
const result = transform(input)
|
|
63
|
+
|
|
64
|
+
assert.strictEqual(result.modified, true)
|
|
65
|
+
assert.match(result.code, /for \(const key in obj\)/)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test("Array.from().forEach() with array destructuring", () => {
|
|
69
|
+
const input = `
|
|
70
|
+
Array.from(Object.entries(obj)).forEach(([key, value]) => {
|
|
71
|
+
console.log(key, value);
|
|
72
|
+
});
|
|
73
|
+
`
|
|
74
|
+
|
|
75
|
+
const result = transform(input)
|
|
76
|
+
|
|
77
|
+
assert.strictEqual(result.modified, true)
|
|
78
|
+
assert.match(
|
|
79
|
+
result.code,
|
|
80
|
+
/for \(const \[key, value\] of Object\.entries\(obj\)\)/,
|
|
81
|
+
)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test("Array.from().forEach() should NOT transform with index parameter", () => {
|
|
85
|
+
const input = `
|
|
86
|
+
Array.from(items).forEach((item, index) => {
|
|
87
|
+
console.log(item, index);
|
|
88
|
+
});
|
|
89
|
+
`
|
|
90
|
+
|
|
91
|
+
const result = transform(input)
|
|
92
|
+
|
|
93
|
+
// Should not transform because callback uses index parameter
|
|
94
|
+
assert.strictEqual(result.modified, false)
|
|
95
|
+
assert.match(result.code, /forEach\(\(item, index\)/)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test("forEach should NOT transform with index parameter", () => {
|
|
99
|
+
const input = `
|
|
100
|
+
items.forEach((item, index) => {
|
|
101
|
+
console.log(item, index);
|
|
102
|
+
});
|
|
103
|
+
`
|
|
104
|
+
|
|
105
|
+
const result = transform(input)
|
|
106
|
+
|
|
107
|
+
// Should not transform because callback uses index parameter
|
|
108
|
+
assert.strictEqual(result.modified, false)
|
|
109
|
+
assert.match(result.code, /forEach\(\(item, index\)/)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
test("forEach should NOT transform unknown objects", () => {
|
|
113
|
+
const input = `
|
|
114
|
+
myCustomObject.forEach(item => {
|
|
115
|
+
console.log(item);
|
|
116
|
+
});
|
|
117
|
+
`
|
|
118
|
+
|
|
119
|
+
const result = transform(input)
|
|
120
|
+
|
|
121
|
+
// Should not transform because we can't be sure myCustomObject is iterable
|
|
122
|
+
assert.strictEqual(result.modified, false)
|
|
123
|
+
assert.match(result.code, /myCustomObject\.forEach/)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
test("forEach should NOT transform Map (uses different signature)", () => {
|
|
127
|
+
const input = `
|
|
128
|
+
myMap.forEach((value, key) => {
|
|
129
|
+
console.log(key, value);
|
|
130
|
+
});
|
|
131
|
+
`
|
|
132
|
+
|
|
133
|
+
const result = transform(input)
|
|
134
|
+
|
|
135
|
+
// Should not transform Map.forEach because it has 2 parameters (value, key)
|
|
136
|
+
assert.strictEqual(result.modified, false)
|
|
137
|
+
assert.match(result.code, /myMap\.forEach/)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
test("Array.from().forEach() should NOT transform without callback", () => {
|
|
141
|
+
const input = `Array.from(items).forEach();`
|
|
142
|
+
|
|
143
|
+
const result = transform(input)
|
|
144
|
+
|
|
145
|
+
assert.strictEqual(result.modified, false)
|
|
146
|
+
assert.match(result.code, /forEach\(\)/)
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
test("Array.from().forEach() should NOT transform with non-function callback", () => {
|
|
150
|
+
const input = `Array.from(items).forEach(callback);`
|
|
151
|
+
|
|
152
|
+
const result = transform(input)
|
|
153
|
+
|
|
154
|
+
assert.strictEqual(result.modified, false)
|
|
155
|
+
assert.match(result.code, /forEach\(callback\)/)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
test("Array.from().forEach() with function expression", () => {
|
|
159
|
+
const input = `Array.from(items).forEach(function(item) {
|
|
160
|
+
console.log(item);
|
|
161
|
+
});`
|
|
162
|
+
|
|
163
|
+
const result = transform(input)
|
|
164
|
+
|
|
165
|
+
assert.strictEqual(result.modified, true)
|
|
166
|
+
assert.match(result.code, /for \(const item of items\)/)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
test("for...of Object.keys() should NOT transform non-Object.keys", () => {
|
|
170
|
+
const input = `
|
|
171
|
+
for (const item of myArray) {
|
|
172
|
+
console.log(item);
|
|
173
|
+
}
|
|
174
|
+
`
|
|
175
|
+
|
|
176
|
+
const result = transform(input)
|
|
177
|
+
|
|
178
|
+
assert.strictEqual(result.modified, false)
|
|
179
|
+
assert.match(result.code, /for \(const item of myArray\)/)
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
test("for...of Object.keys() should NOT transform without arguments", () => {
|
|
183
|
+
const input = `
|
|
184
|
+
for (const key of Object.keys()) {
|
|
185
|
+
console.log(key);
|
|
186
|
+
}
|
|
187
|
+
`
|
|
188
|
+
|
|
189
|
+
const result = transform(input)
|
|
190
|
+
|
|
191
|
+
assert.strictEqual(result.modified, false)
|
|
192
|
+
assert.match(result.code, /Object\.keys\(\)/)
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
test("Array.from().forEach() tracks line numbers", () => {
|
|
196
|
+
const input = `// Line 1
|
|
197
|
+
Array.from(items).forEach(item => console.log(item));`
|
|
198
|
+
|
|
199
|
+
const result = transform(input)
|
|
200
|
+
|
|
201
|
+
assert.strictEqual(result.modified, true)
|
|
202
|
+
assert.strictEqual(result.changes.length, 1)
|
|
203
|
+
assert.strictEqual(result.changes[0].type, "arrayFromForEachToForOf")
|
|
204
|
+
assert.strictEqual(result.changes[0].line, 2)
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
test("for...of Object.keys() tracks line numbers", () => {
|
|
208
|
+
const input = `// Line 1
|
|
209
|
+
for (const key of Object.keys(obj)) {
|
|
210
|
+
console.log(key);
|
|
211
|
+
}`
|
|
212
|
+
|
|
213
|
+
const result = transform(input)
|
|
214
|
+
|
|
215
|
+
assert.strictEqual(result.modified, true)
|
|
216
|
+
assert.strictEqual(result.changes.length, 1)
|
|
217
|
+
assert.strictEqual(result.changes[0].type, "forOfKeysToForIn")
|
|
218
|
+
assert.strictEqual(result.changes[0].line, 2)
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
test("Array.from().forEach() with destructuring and 2+ params transforms if first param is array pattern", () => {
|
|
222
|
+
const input = `Array.from(items).forEach(([a, b], index) => {
|
|
223
|
+
console.log(a, b, index);
|
|
224
|
+
});`
|
|
225
|
+
|
|
226
|
+
const result = transform(input)
|
|
227
|
+
|
|
228
|
+
// According to the code, this should transform because first param is ArrayPattern
|
|
229
|
+
// The logic says: params.length === 1 OR (params.length >= 2 AND first is ArrayPattern)
|
|
230
|
+
assert.strictEqual(result.modified, true)
|
|
231
|
+
assert.match(result.code, /for \(const \[a, b\] of items\)/)
|
|
232
|
+
})
|
|
233
|
+
})
|
|
234
|
+
describe("const and let", () => {
|
|
235
|
+
test("var to const when not reassigned", () => {
|
|
236
|
+
const input = `
|
|
237
|
+
var x = 1;
|
|
238
|
+
`
|
|
239
|
+
|
|
240
|
+
const result = transform(input)
|
|
241
|
+
|
|
242
|
+
assert.strictEqual(result.modified, true)
|
|
243
|
+
assert.match(result.code, /const x = 1/)
|
|
244
|
+
assert.doesNotMatch(result.code, /var x/)
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
test("var to const (simplified version)", () => {
|
|
248
|
+
const input = `
|
|
249
|
+
var x = 1;
|
|
250
|
+
x = 2;
|
|
251
|
+
`
|
|
252
|
+
|
|
253
|
+
const result = transform(input)
|
|
254
|
+
|
|
255
|
+
assert.strictEqual(result.modified, true)
|
|
256
|
+
assert.match(result.code, /const x = 1/)
|
|
257
|
+
assert.doesNotMatch(result.code, /var x/)
|
|
258
|
+
// Note: This will cause a runtime error due to const reassignment
|
|
259
|
+
// A more sophisticated version would detect reassignments and use 'let'
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
test("multiple var declarations", () => {
|
|
263
|
+
const input = `
|
|
264
|
+
var x = 1;
|
|
265
|
+
var y = 2;
|
|
266
|
+
var z = 3;
|
|
267
|
+
`
|
|
268
|
+
|
|
269
|
+
const result = transform(input)
|
|
270
|
+
|
|
271
|
+
assert.strictEqual(result.modified, true)
|
|
272
|
+
assert.match(result.code, /const x = 1/)
|
|
273
|
+
assert.match(result.code, /const y = 2/)
|
|
274
|
+
assert.match(result.code, /const z = 3/)
|
|
275
|
+
assert.strictEqual(result.changes.length, 3)
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
test("var declaration tracks line numbers", () => {
|
|
279
|
+
const input = `// Line 1
|
|
280
|
+
var x = 1;`
|
|
281
|
+
|
|
282
|
+
const result = transform(input)
|
|
283
|
+
|
|
284
|
+
assert.strictEqual(result.modified, true)
|
|
285
|
+
assert.strictEqual(result.changes.length, 1)
|
|
286
|
+
assert.strictEqual(result.changes[0].type, "varToConst")
|
|
287
|
+
assert.strictEqual(result.changes[0].line, 2)
|
|
288
|
+
})
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
describe("template literals", () => {
|
|
292
|
+
test("string concatenation to template literal", () => {
|
|
293
|
+
const input = `const greeting = 'Hello ' + name + '!';`
|
|
294
|
+
|
|
295
|
+
const result = transform(input)
|
|
296
|
+
|
|
297
|
+
assert.strictEqual(result.modified, true)
|
|
298
|
+
assert.match(result.code, /`Hello \$\{name\}!`/)
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
test("multiple string concatenations", () => {
|
|
302
|
+
const input = `const msg = 'Hello ' + firstName + ' ' + lastName + '!';`
|
|
303
|
+
|
|
304
|
+
const result = transform(input)
|
|
305
|
+
|
|
306
|
+
assert.strictEqual(result.modified, true)
|
|
307
|
+
assert.match(result.code, /`Hello \$\{firstName\} \$\{lastName\}!`/)
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
test("concatenation starting with expression", () => {
|
|
311
|
+
const input = `const msg = prefix + ' world';`
|
|
312
|
+
|
|
313
|
+
const result = transform(input)
|
|
314
|
+
|
|
315
|
+
assert.strictEqual(result.modified, true)
|
|
316
|
+
assert.match(result.code, /`\$\{prefix\} world`/)
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
test("concatenation with only expressions", () => {
|
|
320
|
+
const input = `const msg = a + b + c;`
|
|
321
|
+
|
|
322
|
+
const result = transform(input)
|
|
323
|
+
|
|
324
|
+
// Should not transform if no string literals
|
|
325
|
+
assert.strictEqual(result.modified, false)
|
|
326
|
+
assert.match(result.code, /a \+ b \+ c/)
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
test("concatenation ending with expression", () => {
|
|
330
|
+
const input = `const msg = 'Value: ' + value;`
|
|
331
|
+
|
|
332
|
+
const result = transform(input)
|
|
333
|
+
|
|
334
|
+
assert.strictEqual(result.modified, true)
|
|
335
|
+
assert.match(result.code, /`Value: \$\{value\}`/)
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
test("complex nested concatenation", () => {
|
|
339
|
+
const input = `const msg = 'Start ' + (a + 'middle') + ' end';`
|
|
340
|
+
|
|
341
|
+
const result = transform(input)
|
|
342
|
+
|
|
343
|
+
assert.strictEqual(result.modified, true)
|
|
344
|
+
assert.match(result.code, /`/)
|
|
345
|
+
})
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
describe("object spread", () => {
|
|
349
|
+
test("Object.assign to object spread", () => {
|
|
350
|
+
const input = `const obj = Object.assign({}, obj1, obj2);`
|
|
351
|
+
|
|
352
|
+
const result = transform(input)
|
|
353
|
+
|
|
354
|
+
assert.strictEqual(result.modified, true)
|
|
355
|
+
assert.match(result.code, /\.\.\.obj1/)
|
|
356
|
+
assert.match(result.code, /\.\.\.obj2/)
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
test("Object.assign should not transform with non-empty first arg", () => {
|
|
360
|
+
const input = `const obj = Object.assign({ a: 1 }, obj1);`
|
|
361
|
+
|
|
362
|
+
const result = transform(input)
|
|
363
|
+
|
|
364
|
+
assert.strictEqual(result.modified, false)
|
|
365
|
+
assert.match(result.code, /Object\.assign/)
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
test("Object.assign should not transform with non-object first arg", () => {
|
|
369
|
+
const input = `const obj = Object.assign(target, obj1);`
|
|
370
|
+
|
|
371
|
+
const result = transform(input)
|
|
372
|
+
|
|
373
|
+
assert.strictEqual(result.modified, false)
|
|
374
|
+
assert.match(result.code, /Object\.assign/)
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
test("Object.assign with only empty object", () => {
|
|
378
|
+
const input = `const obj = Object.assign({});`
|
|
379
|
+
|
|
380
|
+
const result = transform(input)
|
|
381
|
+
|
|
382
|
+
assert.strictEqual(result.modified, true)
|
|
383
|
+
assert.match(result.code, /\{\}/)
|
|
384
|
+
})
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
test("no changes needed", () => {
|
|
388
|
+
const input = `
|
|
389
|
+
const x = 1;
|
|
390
|
+
const y = 2;
|
|
391
|
+
`
|
|
392
|
+
|
|
393
|
+
const result = transform(input)
|
|
394
|
+
|
|
395
|
+
assert.strictEqual(result.modified, false)
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
test("complex transformation", () => {
|
|
399
|
+
const input = `
|
|
400
|
+
var userName = 'Alice';
|
|
401
|
+
var greeting = 'Hello ' + userName;
|
|
402
|
+
`
|
|
403
|
+
|
|
404
|
+
const result = transform(input)
|
|
405
|
+
|
|
406
|
+
assert.strictEqual(result.modified, true)
|
|
407
|
+
assert.match(result.code, /const userName/)
|
|
408
|
+
assert.match(result.code, /`Hello \$\{userName\}`/)
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
test("baseline option - widely-available", () => {
|
|
412
|
+
const input = `var x = 1;`
|
|
413
|
+
|
|
414
|
+
const result = transform(input)
|
|
415
|
+
|
|
416
|
+
assert.strictEqual(result.modified, true)
|
|
417
|
+
assert.match(result.code, /const x = 1/)
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
test("baseline option - newly-available", () => {
|
|
421
|
+
const input = `var x = 1;`
|
|
422
|
+
|
|
423
|
+
const result = transform(input, "newly-available")
|
|
424
|
+
|
|
425
|
+
assert.strictEqual(result.modified, true)
|
|
426
|
+
assert.match(result.code, /const x = 1/)
|
|
427
|
+
})
|
|
428
|
+
})
|
package/.idea/esupgrade.iml
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<module type="PYTHON_MODULE" version="4">
|
|
3
|
-
<component name="NewModuleRootManager">
|
|
4
|
-
<content url="file://$MODULE_DIR$" />
|
|
5
|
-
<orderEntry type="jdk" jdkName="Python 3.9" jdkType="Python SDK" />
|
|
6
|
-
<orderEntry type="sourceFolder" forTests="false" />
|
|
7
|
-
</component>
|
|
8
|
-
</module>
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
<component name="InspectionProjectProfileManager">
|
|
2
|
-
<profile version="1.0">
|
|
3
|
-
<option name="myName" value="Project Default" />
|
|
4
|
-
<inspection_tool class="DjangoUnresolvedUrlInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
|
5
|
-
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
|
6
|
-
<inspection_tool class="JsonSchemaCompliance" enabled="false" level="WARNING" enabled_by_default="false" />
|
|
7
|
-
<inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
|
8
|
-
<option name="ourVersions">
|
|
9
|
-
<value>
|
|
10
|
-
<list size="2">
|
|
11
|
-
<item index="0" class="java.lang.String" itemvalue="3.13" />
|
|
12
|
-
<item index="1" class="java.lang.String" itemvalue="3.14" />
|
|
13
|
-
</list>
|
|
14
|
-
</value>
|
|
15
|
-
</option>
|
|
16
|
-
</inspection_tool>
|
|
17
|
-
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
|
18
|
-
<option name="ignoredIdentifiers">
|
|
19
|
-
<list>
|
|
20
|
-
<option value="ssdp.lexers.content_callback" />
|
|
21
|
-
</list>
|
|
22
|
-
</option>
|
|
23
|
-
</inspection_tool>
|
|
24
|
-
<inspection_tool class="StandardJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
|
25
|
-
<inspection_tool class="Stylelint" enabled="true" level="ERROR" enabled_by_default="true" />
|
|
26
|
-
<inspection_tool class="TsLint" enabled="true" level="WARNING" enabled_by_default="true" />
|
|
27
|
-
</profile>
|
|
28
|
-
</component>
|