esupgrade 2025.3.0 → 2025.3.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 +30 -70
- package/tests/cli.test.js +150 -110
- package/tests/index.test.js +230 -213
- package/tests/newlyAvailable.test.js +325 -167
- package/tests/widelyAvailable.test.js +1073 -1400
package/tests/index.test.js
CHANGED
|
@@ -1,274 +1,291 @@
|
|
|
1
1
|
import { describe, test } from "node:test"
|
|
2
|
-
import assert from "node:assert"
|
|
2
|
+
import assert from "node:assert/strict"
|
|
3
3
|
import { transform } from "../src/index.js"
|
|
4
4
|
|
|
5
|
-
describe("
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
assert.strictEqual(typeof result.modified, "boolean")
|
|
16
|
-
assert.ok(Array.isArray(result.changes))
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
test("should handle empty string", () => {
|
|
20
|
-
const input = ``
|
|
21
|
-
const result = transform(input)
|
|
22
|
-
|
|
23
|
-
assert.strictEqual(result.modified, false)
|
|
24
|
-
assert.strictEqual(result.changes.length, 0)
|
|
25
|
-
assert.strictEqual(result.code, "")
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
test("should handle whitespace-only string", () => {
|
|
29
|
-
const input = ` \n\n `
|
|
30
|
-
const result = transform(input)
|
|
5
|
+
describe("transform", () => {
|
|
6
|
+
test("return TransformResult with correct structure", () => {
|
|
7
|
+
const result = transform(`var x = 1;`)
|
|
8
|
+
assert(result.hasOwnProperty("code"), "result has code property")
|
|
9
|
+
assert(result.hasOwnProperty("modified"), "result has modified property")
|
|
10
|
+
assert(result.hasOwnProperty("changes"), "result has changes property")
|
|
11
|
+
assert(typeof result.code === "string", "code is string")
|
|
12
|
+
assert(typeof result.modified === "boolean", "modified is boolean")
|
|
13
|
+
assert(Array.isArray(result.changes), "changes is array")
|
|
14
|
+
})
|
|
31
15
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
16
|
+
test("handle empty string", () => {
|
|
17
|
+
const result = transform(``)
|
|
18
|
+
assert(!result.modified, "modified is false for empty string")
|
|
19
|
+
assert(result.changes.length === 0, "changes length is 0")
|
|
20
|
+
assert(result.code === "", "code is empty")
|
|
21
|
+
})
|
|
35
22
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
23
|
+
test("handle whitespace-only string", () => {
|
|
24
|
+
const result = transform(` \n\n `)
|
|
25
|
+
assert(!result.modified, "modified is false for whitespace")
|
|
26
|
+
assert(result.changes.length === 0, "changes length is 0")
|
|
27
|
+
})
|
|
39
28
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
29
|
+
test("handle comments only", () => {
|
|
30
|
+
const result = transform(`// This is a comment\n/* Another comment */`)
|
|
31
|
+
assert(!result.modified, "modified is false for comments")
|
|
32
|
+
assert(result.changes.length === 0, "changes length is 0")
|
|
33
|
+
})
|
|
43
34
|
|
|
44
|
-
|
|
45
|
-
|
|
35
|
+
test("apply multiple transformations and track all changes", () => {
|
|
36
|
+
const result = transform(`
|
|
46
37
|
var x = 1;
|
|
47
38
|
var y = 'Hello ' + name;
|
|
48
39
|
var obj = Object.assign({}, data);
|
|
49
|
-
`
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// Verify the transformations happened even if not all are tracked
|
|
60
|
-
assert.match(result.code, /const/)
|
|
61
|
-
assert.match(result.code, /`Hello/)
|
|
62
|
-
assert.match(result.code, /\.\.\.data/)
|
|
63
|
-
})
|
|
40
|
+
`)
|
|
41
|
+
assert(result.modified, "modified is true")
|
|
42
|
+
assert(result.changes.length > 0, "changes length greater than 0")
|
|
43
|
+
const changeTypes = result.changes.map((c) => c.type)
|
|
44
|
+
assert(changeTypes.includes("varToConst"), "changes include varToConst")
|
|
45
|
+
assert.match(result.code, /const/, "code contains const")
|
|
46
|
+
assert.match(result.code, /`Hello/, "code contains template literal")
|
|
47
|
+
assert.match(result.code, /\.\.\.data/, "code contains spread")
|
|
48
|
+
})
|
|
64
49
|
|
|
65
|
-
|
|
66
|
-
|
|
50
|
+
test("aggregate changes from multiple transformers", () => {
|
|
51
|
+
const result = transform(
|
|
52
|
+
`
|
|
67
53
|
var a = 1;
|
|
68
54
|
var b = 'Hello ' + world;
|
|
69
55
|
Array.from(items).forEach(item => console.log(item));
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
56
|
+
`,
|
|
57
|
+
"widely-available",
|
|
58
|
+
)
|
|
59
|
+
assert(result.modified, "modified is true")
|
|
60
|
+
assert(
|
|
61
|
+
result.changes.every((c) => c.hasOwnProperty("line")),
|
|
62
|
+
"all changes have line property",
|
|
63
|
+
)
|
|
64
|
+
assert(
|
|
65
|
+
result.changes.every((c) => c.hasOwnProperty("type")),
|
|
66
|
+
"all changes have type property",
|
|
67
|
+
)
|
|
68
|
+
})
|
|
83
69
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
70
|
+
test("use widely-available transformers by default", () => {
|
|
71
|
+
const result = transform(`var x = 1;`)
|
|
72
|
+
assert(result.modified, "modified is true")
|
|
73
|
+
assert(result.changes[0].type === "varToConst", "first change type is varToConst")
|
|
74
|
+
})
|
|
87
75
|
|
|
88
|
-
|
|
89
|
-
|
|
76
|
+
test("include newly-available transformers when specified", () => {
|
|
77
|
+
const result = transform(
|
|
78
|
+
`
|
|
90
79
|
var x = 1;
|
|
91
80
|
const p = new Promise((resolve) => resolve(getData()));
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
})
|
|
81
|
+
`,
|
|
82
|
+
"newly-available",
|
|
83
|
+
)
|
|
84
|
+
assert(result.modified, "modified is true")
|
|
85
|
+
const changeTypes = result.changes.map((c) => c.type)
|
|
86
|
+
assert(changeTypes.includes("varToConst"), "changes include varToConst")
|
|
87
|
+
assert(changeTypes.includes("promiseTry"), "changes include promiseTry")
|
|
88
|
+
})
|
|
101
89
|
|
|
102
|
-
|
|
103
|
-
|
|
90
|
+
test("handle complex nested structures", () => {
|
|
91
|
+
const result = transform(`
|
|
104
92
|
function test() {
|
|
105
93
|
var result = Object.assign({}, {
|
|
106
94
|
message: 'Hello ' + name
|
|
107
95
|
});
|
|
108
96
|
return result;
|
|
109
97
|
}
|
|
110
|
-
`
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
assert.match(result.code, /`Hello/)
|
|
117
|
-
})
|
|
98
|
+
`)
|
|
99
|
+
assert(result.modified, "modified is true")
|
|
100
|
+
assert.match(result.code, /const result/, "code contains const result")
|
|
101
|
+
assert.match(result.code, /\.\.\./, "code contains spread")
|
|
102
|
+
assert.match(result.code, /`Hello/, "code contains template literal")
|
|
103
|
+
})
|
|
118
104
|
|
|
119
|
-
|
|
120
|
-
|
|
105
|
+
test("handle JSX syntax", () => {
|
|
106
|
+
const result = transform(`
|
|
121
107
|
var Component = () => {
|
|
122
108
|
var title = 'Hello ' + name;
|
|
123
109
|
return <div>{title}</div>;
|
|
124
110
|
};
|
|
125
|
-
`
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
assert.match(result.code, /<div>/)
|
|
131
|
-
})
|
|
111
|
+
`)
|
|
112
|
+
assert(result.modified, "modified is true")
|
|
113
|
+
assert.match(result.code, /const/, "code contains const")
|
|
114
|
+
assert.match(result.code, /<div>/, "code contains JSX")
|
|
115
|
+
})
|
|
132
116
|
|
|
133
|
-
|
|
134
|
-
|
|
117
|
+
test("handle TypeScript syntax", () => {
|
|
118
|
+
const result = transform(`
|
|
135
119
|
var x: number = 1;
|
|
136
120
|
const greeting: string = 'Hello ' + name;
|
|
137
|
-
`
|
|
138
|
-
|
|
121
|
+
`)
|
|
122
|
+
assert(result.modified, "modified is true")
|
|
123
|
+
assert.match(result.code, /const x: number/, "code contains const x: number")
|
|
124
|
+
assert.match(result.code, /`Hello/, "code contains template literal")
|
|
125
|
+
})
|
|
139
126
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
127
|
+
test("preserve code formatting structure", () => {
|
|
128
|
+
const result = transform(`var x = 1;
|
|
129
|
+
var y = 2;`)
|
|
130
|
+
assert.match(result.code, /const x = 1/, "code contains const x = 1")
|
|
131
|
+
assert.match(result.code, /const y = 2/, "code contains const y = 2")
|
|
132
|
+
})
|
|
144
133
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
134
|
+
test("handle very large code", () => {
|
|
135
|
+
const lines = []
|
|
136
|
+
for (let i = 0; i < 100; i++) {
|
|
137
|
+
lines.push(`var x${i} = ${i};`)
|
|
138
|
+
}
|
|
139
|
+
const result = transform(lines.join("\n"))
|
|
140
|
+
assert(result.modified, "modified is true")
|
|
141
|
+
assert(result.changes.length === 100, "changes length is 100")
|
|
142
|
+
assert.match(result.code, /const x0 = 0/, "code contains const x0 = 0")
|
|
143
|
+
assert.match(result.code, /const x99 = 99/, "code contains const x99 = 99")
|
|
144
|
+
})
|
|
149
145
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
146
|
+
test("handle code with special characters", () => {
|
|
147
|
+
const result = transform(`var msg = 'Hello \\n' + 'World\\t!';`)
|
|
148
|
+
assert(result.modified, "modified is true")
|
|
149
|
+
assert.match(result.code, /const msg/, "code contains const msg")
|
|
150
|
+
assert.match(result.code, /`Hello/, "code contains template literal")
|
|
151
|
+
})
|
|
154
152
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
const input = lines.join("\n")
|
|
153
|
+
test("handle code with unicode characters", () => {
|
|
154
|
+
const result = transform(`var msg = 'Hello ' + '世界' + '!';`)
|
|
155
|
+
assert(result.modified, "modified is true")
|
|
156
|
+
assert.match(result.code, /const msg/, "code contains const msg")
|
|
157
|
+
})
|
|
162
158
|
|
|
163
|
-
|
|
159
|
+
test("handle all transformers returning no changes", () => {
|
|
160
|
+
const result = transform(`const x = 1; const y = 2;`)
|
|
161
|
+
assert(!result.modified, "modified is false")
|
|
162
|
+
assert(result.changes.length === 0, "changes length is 0")
|
|
163
|
+
})
|
|
164
164
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
165
|
+
test("handle baseline parameter case sensitivity", () => {
|
|
166
|
+
const result1 = transform(`var x = 1;`, "widely-available")
|
|
167
|
+
assert(result1.modified, "modified is true for widely-available")
|
|
168
|
+
const result2 = transform(`var x = 1;`, "newly-available")
|
|
169
|
+
assert(result2.modified, "modified is true for newly-available")
|
|
170
|
+
})
|
|
170
171
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
172
|
+
test("merge transformers correctly for newly-available", () => {
|
|
173
|
+
const result = transform(
|
|
174
|
+
`
|
|
175
|
+
var x = 1;
|
|
176
|
+
var obj = Object.assign({}, data);
|
|
177
|
+
const p = new Promise((resolve) => resolve(getData()));
|
|
178
|
+
`,
|
|
179
|
+
"newly-available",
|
|
180
|
+
)
|
|
181
|
+
assert(result.modified, "modified is true")
|
|
182
|
+
const changeTypes = result.changes.map((c) => c.type)
|
|
183
|
+
assert(changeTypes.includes("varToConst"), "changes include varToConst")
|
|
184
|
+
assert(changeTypes.includes("promiseTry"), "changes include promiseTry")
|
|
185
|
+
assert.match(result.code, /const/, "code contains const")
|
|
186
|
+
assert.match(result.code, /\.\.\.data/, "code contains spread")
|
|
187
|
+
assert.match(result.code, /Promise\.try/, "code contains Promise.try")
|
|
188
|
+
})
|
|
174
189
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
})
|
|
190
|
+
test("handle code without location info gracefully", () => {
|
|
191
|
+
const result = transform(`var x = 1;`)
|
|
192
|
+
assert(result.modified, "modified is true")
|
|
193
|
+
})
|
|
180
194
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
195
|
+
test("generate valid JavaScript output", () => {
|
|
196
|
+
const result = transform(`
|
|
197
|
+
var x = 1;
|
|
198
|
+
var greeting = 'Hello ' + name;
|
|
199
|
+
Array.from(items).forEach(item => console.log(item));
|
|
200
|
+
`)
|
|
201
|
+
assert.doesNotThrow(() => {
|
|
202
|
+
new Function(result.code)
|
|
203
|
+
}, "code is valid JavaScript")
|
|
204
|
+
})
|
|
184
205
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
206
|
+
test("handle single-line code", () => {
|
|
207
|
+
const result = transform(`var x = 1; var y = 2; var z = 3;`)
|
|
208
|
+
assert(result.modified, "modified is true")
|
|
209
|
+
assert.match(result.code, /const x = 1/, "code contains const x = 1")
|
|
210
|
+
assert.match(result.code, /const y = 2/, "code contains const y = 2")
|
|
211
|
+
assert.match(result.code, /const z = 3/, "code contains const z = 3")
|
|
212
|
+
})
|
|
188
213
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
214
|
+
test("handle code with existing template literals", () => {
|
|
215
|
+
const result = transform(`
|
|
216
|
+
var msg = \`Hello \${name}\`;
|
|
217
|
+
var other = 'Test ' + value;
|
|
218
|
+
`)
|
|
219
|
+
assert(result.modified, "modified is true")
|
|
220
|
+
assert.match(result.code, /const msg/, "code contains const msg")
|
|
221
|
+
assert.match(result.code, /const other/, "code contains const other")
|
|
222
|
+
})
|
|
223
|
+
})
|
|
192
224
|
|
|
193
|
-
|
|
194
|
-
|
|
225
|
+
describe("transformers", () => {
|
|
226
|
+
describe("varToConst", () => {
|
|
227
|
+
test("transform var to const", () => {
|
|
228
|
+
assert(transform(`var x = 1;`).modified)
|
|
229
|
+
assert.match(transform(`var x = 1;`).code, /const/)
|
|
195
230
|
})
|
|
196
231
|
|
|
197
|
-
test("
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const
|
|
202
|
-
assert.strictEqual(result1.modified, true)
|
|
203
|
-
|
|
204
|
-
const result2 = transform(input, "newly-available")
|
|
205
|
-
assert.strictEqual(result2.modified, true)
|
|
232
|
+
test("transform multiple vars to consts", () => {
|
|
233
|
+
const result = transform(`var x = 1; var y = 2;`)
|
|
234
|
+
assert(result.modified)
|
|
235
|
+
assert.match(result.code, /const x = 1/)
|
|
236
|
+
assert.match(result.code, /const y = 2/)
|
|
206
237
|
})
|
|
238
|
+
})
|
|
207
239
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
const result = transform(input, "newly-available")
|
|
215
|
-
|
|
216
|
-
assert.strictEqual(result.modified, true)
|
|
217
|
-
|
|
218
|
-
// Should have changes from transformers that track line numbers
|
|
219
|
-
const changeTypes = result.changes.map((c) => c.type)
|
|
220
|
-
assert.ok(changeTypes.includes("varToConst")) // from widely-available
|
|
221
|
-
assert.ok(changeTypes.includes("promiseTry")) // from newly-available
|
|
222
|
-
|
|
223
|
-
// Verify all transformations happened
|
|
224
|
-
assert.match(result.code, /const/)
|
|
225
|
-
assert.match(result.code, /\.\.\.data/)
|
|
226
|
-
assert.match(result.code, /Promise\.try/)
|
|
240
|
+
describe("stringConcatToTemplate", () => {
|
|
241
|
+
test("transform string concat to template literal", () => {
|
|
242
|
+
const result = transform(`var msg = 'Hello ' + name;`)
|
|
243
|
+
assert(result.modified)
|
|
244
|
+
assert.match(result.code, /const msg/)
|
|
245
|
+
assert.match(result.code, /`Hello/)
|
|
227
246
|
})
|
|
228
247
|
|
|
229
|
-
test("
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
assert.strictEqual(result.modified, true)
|
|
235
|
-
// Should handle missing loc info gracefully
|
|
248
|
+
test("transform complex concat to template", () => {
|
|
249
|
+
const result = transform(`var greeting = 'Hello ' + name + '!';`)
|
|
250
|
+
assert(result.modified)
|
|
251
|
+
assert.match(result.code, /const greeting/)
|
|
252
|
+
assert.match(result.code, /`Hello/)
|
|
236
253
|
})
|
|
254
|
+
})
|
|
237
255
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const result = transform(input)
|
|
245
|
-
|
|
246
|
-
// Try to parse the output to ensure it's valid JS
|
|
247
|
-
assert.doesNotThrow(() => {
|
|
248
|
-
new Function(result.code)
|
|
249
|
-
})
|
|
256
|
+
describe("objectAssignToSpread", () => {
|
|
257
|
+
test("transform Object.assign to spread", () => {
|
|
258
|
+
const result = transform(`var obj = Object.assign({}, data);`)
|
|
259
|
+
assert(result.modified)
|
|
260
|
+
assert.match(result.code, /const obj/)
|
|
261
|
+
assert.match(result.code, /\.\.\.data/)
|
|
250
262
|
})
|
|
251
263
|
|
|
252
|
-
test("
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
assert.
|
|
257
|
-
assert.match(result.code, /const x = 1/)
|
|
258
|
-
assert.match(result.code, /const y = 2/)
|
|
259
|
-
assert.match(result.code, /const z = 3/)
|
|
264
|
+
test("transform nested Object.assign", () => {
|
|
265
|
+
const result = transform(`var result = Object.assign({}, { message: 'test' });`)
|
|
266
|
+
assert(result.modified)
|
|
267
|
+
assert.match(result.code, /const result/)
|
|
268
|
+
assert.match(result.code, /\.\.\./)
|
|
260
269
|
})
|
|
270
|
+
})
|
|
261
271
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
272
|
+
describe("promiseTry", () => {
|
|
273
|
+
test("transform Promise constructor to Promise.try", () => {
|
|
274
|
+
const result = transform(
|
|
275
|
+
`const p = new Promise((resolve) => resolve(getData()));`,
|
|
276
|
+
"newly-available",
|
|
277
|
+
)
|
|
278
|
+
assert(result.modified)
|
|
279
|
+
assert.match(result.code, /Promise\.try/)
|
|
280
|
+
})
|
|
268
281
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
282
|
+
test("transform Promise with resolve", () => {
|
|
283
|
+
const result = transform(
|
|
284
|
+
`const p = new Promise((resolve) => resolve(data));`,
|
|
285
|
+
"newly-available",
|
|
286
|
+
)
|
|
287
|
+
assert(result.modified)
|
|
288
|
+
assert.match(result.code, /Promise\.try/)
|
|
272
289
|
})
|
|
273
290
|
})
|
|
274
291
|
})
|