esupgrade 2025.0.2 → 2025.1.0
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/README.md +28 -7
- package/images/logo-dark.svg +2 -2
- package/images/logo-light.svg +2 -2
- package/package.json +1 -1
- package/src/newlyAvailable.js +1 -0
- package/src/widelyAvailable.js +72 -0
- package/tests/widelyAvailable.test.js +137 -0
package/README.md
CHANGED
|
@@ -73,7 +73,7 @@ For more information about Baseline browser support, visit [web.dev/baseline][ba
|
|
|
73
73
|
|
|
74
74
|
### Widely available
|
|
75
75
|
|
|
76
|
-
#### `var` →
|
|
76
|
+
#### `var` → [const][mdn-const] & [let][mdn-let]
|
|
77
77
|
|
|
78
78
|
```diff
|
|
79
79
|
-var x = 1;
|
|
@@ -84,7 +84,7 @@ For more information about Baseline browser support, visit [web.dev/baseline][ba
|
|
|
84
84
|
+y = 3;
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
#### String concatenation → Template literals
|
|
87
|
+
#### String concatenation → [Template literals][mdn-template-literals]
|
|
88
88
|
|
|
89
89
|
```diff
|
|
90
90
|
-const greeting = 'Hello ' + name + '!';
|
|
@@ -93,7 +93,7 @@ For more information about Baseline browser support, visit [web.dev/baseline][ba
|
|
|
93
93
|
+const message = `You have ${count} items`;
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
#### `Array.from().forEach()` → `for...of` loops
|
|
96
|
+
#### `Array.from().forEach()` → [`for...of` loops][mdn-for-of]
|
|
97
97
|
|
|
98
98
|
```diff
|
|
99
99
|
-Array.from(items).forEach(item => {
|
|
@@ -104,7 +104,21 @@ For more information about Baseline browser support, visit [web.dev/baseline][ba
|
|
|
104
104
|
+}
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
-
#### `
|
|
107
|
+
#### `Array.from()` → [Array spread [...]][mdn-spread]
|
|
108
|
+
|
|
109
|
+
```diff
|
|
110
|
+
-const doubled = Array.from(numbers).map(n => n * 2);
|
|
111
|
+
-const filtered = Array.from(items).filter(x => x > 5);
|
|
112
|
+
-const arr = Array.from(iterable);
|
|
113
|
+
+const doubled = [...numbers].map(n => n * 2);
|
|
114
|
+
+const filtered = [...items].filter(x => x > 5);
|
|
115
|
+
+const arr = [...iterable];
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
> [!NOTE]
|
|
119
|
+
> `Array.from()` with a mapping function or thisArg is not converted.
|
|
120
|
+
|
|
121
|
+
#### `Object.assign({}, ...)` → [Object spread {...}][mdn-spread]
|
|
108
122
|
|
|
109
123
|
```diff
|
|
110
124
|
-const obj = Object.assign({}, obj1, obj2);
|
|
@@ -113,7 +127,7 @@ For more information about Baseline browser support, visit [web.dev/baseline][ba
|
|
|
113
127
|
+const copy = { ...original };
|
|
114
128
|
```
|
|
115
129
|
|
|
116
|
-
#### `.concat()` → Array spread
|
|
130
|
+
#### `.concat()` → [Array spread [...]][mdn-spread]
|
|
117
131
|
|
|
118
132
|
```diff
|
|
119
133
|
-const combined = arr1.concat(arr2, arr3);
|
|
@@ -122,7 +136,7 @@ For more information about Baseline browser support, visit [web.dev/baseline][ba
|
|
|
122
136
|
+const withItem = [...array, item];
|
|
123
137
|
```
|
|
124
138
|
|
|
125
|
-
#### Function expressions → Arrow functions
|
|
139
|
+
#### Function expressions → [Arrow functions][mdn-arrow-functions]
|
|
126
140
|
|
|
127
141
|
```diff
|
|
128
142
|
-const fn = function(x) { return x * 2; };
|
|
@@ -145,7 +159,7 @@ For more information about Baseline browser support, visit [web.dev/baseline][ba
|
|
|
145
159
|
> [!CAUTION]
|
|
146
160
|
> These transformations are mainly to harden code for future releases and should be used with caution.
|
|
147
161
|
|
|
148
|
-
#### `new Promise((resolve) => { ... })` →
|
|
162
|
+
#### `new Promise((resolve) => { ... })` → [Promise.try][mdn-promise-try]
|
|
149
163
|
|
|
150
164
|
```diff
|
|
151
165
|
-new Promise((resolve) => {
|
|
@@ -158,4 +172,11 @@ For more information about Baseline browser support, visit [web.dev/baseline][ba
|
|
|
158
172
|
```
|
|
159
173
|
|
|
160
174
|
[baseline]: https://web.dev/baseline/
|
|
175
|
+
[mdn-arrow-functions]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
|
|
176
|
+
[mdn-const]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
|
|
177
|
+
[mdn-for-of]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
|
|
178
|
+
[mdn-let]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
|
|
179
|
+
[mdn-promise-try]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/try
|
|
180
|
+
[mdn-spread]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
|
|
181
|
+
[mdn-template-literals]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
|
|
161
182
|
[pre-commit]: https://pre-commit.com/
|
package/images/logo-dark.svg
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" width="640" height="320" font-family="Segoe UI, system-ui, sans-serif" viewBox="0 0 1280 640">
|
|
2
2
|
<circle cx="260" cy="320" r="200" fill="#f7df1e"/>
|
|
3
3
|
<path fill="none" stroke="#191919" stroke-width="48" d="m167 409 95-95 95 95m-190 -95 95-95 95 95m415 50"/>
|
|
4
|
-
<text x="241" y="148" fill="#c9d1d9" font-size="
|
|
4
|
+
<text x="241" y="148" fill="#c9d1d9" font-size="90" font-weight="bold" transform="matrix(1.6 0 0 1.6 120 120)">
|
|
5
5
|
esupgrade
|
|
6
6
|
</text>
|
|
7
|
-
<text x="241" y="192" fill="#c9d1d9" font-size="
|
|
7
|
+
<text x="241" y="192" fill="#c9d1d9" font-size="27" font-style="italic" transform="matrix(1.6 0 0 1.6 120 120)">
|
|
8
8
|
Auto-upgrade your JavaScript syntax
|
|
9
9
|
</text>
|
|
10
10
|
</svg>
|
package/images/logo-light.svg
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" width="640" height="320" font-family="Segoe UI, system-ui, sans-serif" viewBox="0 0 1280 640">
|
|
2
2
|
<circle cx="260" cy="320" r="200" fill="#f7df1e"/>
|
|
3
3
|
<path fill="none" stroke="#191919" stroke-width="48" d="m167 409 95-95 95 95m-190 -95 95-95 95 95m415 50"/>
|
|
4
|
-
<text x="241" y="148" font-size="
|
|
4
|
+
<text x="241" y="148" font-size="90" font-weight="bold" transform="matrix(1.6 0 0 1.6 120 120)">
|
|
5
5
|
esupgrade
|
|
6
6
|
</text>
|
|
7
|
-
<text x="241" y="192" font-size="
|
|
7
|
+
<text x="241" y="192" font-size="27" font-style="italic" transform="matrix(1.6 0 0 1.6 120 120)">
|
|
8
8
|
Auto-upgrade your JavaScript syntax
|
|
9
9
|
</text>
|
|
10
10
|
</svg>
|
package/package.json
CHANGED
package/src/newlyAvailable.js
CHANGED
package/src/widelyAvailable.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Transform var to const
|
|
3
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
|
|
4
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
|
|
3
5
|
*/
|
|
4
6
|
export function varToConst(j, root) {
|
|
5
7
|
let modified = false
|
|
@@ -21,6 +23,7 @@ export function varToConst(j, root) {
|
|
|
21
23
|
|
|
22
24
|
/**
|
|
23
25
|
* Transform string concatenation to template literals
|
|
26
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
|
|
24
27
|
*/
|
|
25
28
|
export function concatToTemplateLiteral(j, root) {
|
|
26
29
|
let modified = false
|
|
@@ -94,6 +97,7 @@ export function concatToTemplateLiteral(j, root) {
|
|
|
94
97
|
|
|
95
98
|
/**
|
|
96
99
|
* Transform Object.assign({}, ...) to object spread
|
|
100
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
|
|
97
101
|
*/
|
|
98
102
|
export function objectAssignToSpread(j, root) {
|
|
99
103
|
let modified = false
|
|
@@ -128,6 +132,7 @@ export function objectAssignToSpread(j, root) {
|
|
|
128
132
|
|
|
129
133
|
/**
|
|
130
134
|
* Transform Array.from().forEach() to for...of
|
|
135
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
|
|
131
136
|
*/
|
|
132
137
|
export function arrayFromForEachToForOf(j, root) {
|
|
133
138
|
let modified = false
|
|
@@ -215,6 +220,7 @@ export function arrayFromForEachToForOf(j, root) {
|
|
|
215
220
|
|
|
216
221
|
/**
|
|
217
222
|
* Transform for...of Object.keys() loops to for...in
|
|
223
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
|
|
218
224
|
*/
|
|
219
225
|
export function forOfKeysToForIn(j, root) {
|
|
220
226
|
let modified = false
|
|
@@ -263,3 +269,69 @@ export function forOfKeysToForIn(j, root) {
|
|
|
263
269
|
|
|
264
270
|
return { modified, changes }
|
|
265
271
|
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Transform Array.from(obj) to [...obj] spread syntax
|
|
275
|
+
* This handles cases like Array.from(obj).map(), .filter(), .some(), etc.
|
|
276
|
+
* that are not covered by the forEach transformer
|
|
277
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
|
|
278
|
+
*/
|
|
279
|
+
export function arrayFromToSpread(j, root) {
|
|
280
|
+
let modified = false
|
|
281
|
+
const changes = []
|
|
282
|
+
|
|
283
|
+
root
|
|
284
|
+
.find(j.CallExpression)
|
|
285
|
+
.filter((path) => {
|
|
286
|
+
const node = path.node
|
|
287
|
+
|
|
288
|
+
// Check if this is Array.from() call
|
|
289
|
+
if (
|
|
290
|
+
!j.MemberExpression.check(node.callee) ||
|
|
291
|
+
!j.Identifier.check(node.callee.object) ||
|
|
292
|
+
node.callee.object.name !== "Array" ||
|
|
293
|
+
!j.Identifier.check(node.callee.property) ||
|
|
294
|
+
node.callee.property.name !== "from"
|
|
295
|
+
) {
|
|
296
|
+
return false
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Must have exactly one argument (the iterable)
|
|
300
|
+
// If there's a second argument (mapping function), we should not transform
|
|
301
|
+
if (node.arguments.length !== 1) {
|
|
302
|
+
return false
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Don't transform if this is Array.from().forEach()
|
|
306
|
+
// as that's handled by arrayFromForEachToForOf
|
|
307
|
+
const parent = path.parent.node
|
|
308
|
+
if (
|
|
309
|
+
j.MemberExpression.check(parent) &&
|
|
310
|
+
j.Identifier.check(parent.property) &&
|
|
311
|
+
parent.property.name === "forEach"
|
|
312
|
+
) {
|
|
313
|
+
return false
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return true
|
|
317
|
+
})
|
|
318
|
+
.forEach((path) => {
|
|
319
|
+
const node = path.node
|
|
320
|
+
const iterable = node.arguments[0]
|
|
321
|
+
|
|
322
|
+
// Create array with spread element
|
|
323
|
+
const spreadArray = j.arrayExpression([j.spreadElement(iterable)])
|
|
324
|
+
|
|
325
|
+
j(path).replaceWith(spreadArray)
|
|
326
|
+
|
|
327
|
+
modified = true
|
|
328
|
+
if (node.loc) {
|
|
329
|
+
changes.push({
|
|
330
|
+
type: "arrayFromToSpread",
|
|
331
|
+
line: node.loc.start.line,
|
|
332
|
+
})
|
|
333
|
+
}
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
return { modified, changes }
|
|
337
|
+
}
|
|
@@ -231,6 +231,143 @@ for (const key of Object.keys(obj)) {
|
|
|
231
231
|
assert.match(result.code, /for \(const \[a, b\] of items\)/)
|
|
232
232
|
})
|
|
233
233
|
})
|
|
234
|
+
|
|
235
|
+
describe("Array.from() to spread", () => {
|
|
236
|
+
test("Array.from().map() to [...].map()", () => {
|
|
237
|
+
const input = `const doubled = Array.from(numbers).map(n => n * 2);`
|
|
238
|
+
|
|
239
|
+
const result = transform(input)
|
|
240
|
+
|
|
241
|
+
assert.strictEqual(result.modified, true)
|
|
242
|
+
assert.match(result.code, /\[\.\.\.numbers\]\.map/)
|
|
243
|
+
assert.doesNotMatch(result.code, /Array\.from/)
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
test("Array.from().filter() to [...].filter()", () => {
|
|
247
|
+
const input = `const filtered = Array.from(items).filter(x => x > 5);`
|
|
248
|
+
|
|
249
|
+
const result = transform(input)
|
|
250
|
+
|
|
251
|
+
assert.strictEqual(result.modified, true)
|
|
252
|
+
assert.match(result.code, /\[\.\.\.items\]\.filter/)
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
test("Array.from().some() to [...].some()", () => {
|
|
256
|
+
const input = `const hasValue = Array.from(collection).some(item => item.active);`
|
|
257
|
+
|
|
258
|
+
const result = transform(input)
|
|
259
|
+
|
|
260
|
+
assert.strictEqual(result.modified, true)
|
|
261
|
+
assert.match(result.code, /\[\.\.\.collection\]\.some/)
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
test("Array.from().every() to [...].every()", () => {
|
|
265
|
+
const input = `const allValid = Array.from(items).every(x => x.valid);`
|
|
266
|
+
|
|
267
|
+
const result = transform(input)
|
|
268
|
+
|
|
269
|
+
assert.strictEqual(result.modified, true)
|
|
270
|
+
assert.match(result.code, /\[\.\.\.items\]\.every/)
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
test("Array.from().find() to [...].find()", () => {
|
|
274
|
+
const input = `const found = Array.from(elements).find(el => el.id === 'target');`
|
|
275
|
+
|
|
276
|
+
const result = transform(input)
|
|
277
|
+
|
|
278
|
+
assert.strictEqual(result.modified, true)
|
|
279
|
+
assert.match(result.code, /\[\.\.\.elements\]\.find/)
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
test("Array.from().reduce() to [...].reduce()", () => {
|
|
283
|
+
const input = `const sum = Array.from(values).reduce((a, b) => a + b, 0);`
|
|
284
|
+
|
|
285
|
+
const result = transform(input)
|
|
286
|
+
|
|
287
|
+
assert.strictEqual(result.modified, true)
|
|
288
|
+
assert.match(result.code, /\[\.\.\.values\]\.reduce/)
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
test("Array.from() standalone to [...]", () => {
|
|
292
|
+
const input = `const arr = Array.from(iterable);`
|
|
293
|
+
|
|
294
|
+
const result = transform(input)
|
|
295
|
+
|
|
296
|
+
assert.strictEqual(result.modified, true)
|
|
297
|
+
assert.match(result.code, /const arr = \[\.\.\.iterable\]/)
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
test("Array.from() with property access", () => {
|
|
301
|
+
const input = `const length = Array.from(items).length;`
|
|
302
|
+
|
|
303
|
+
const result = transform(input)
|
|
304
|
+
|
|
305
|
+
assert.strictEqual(result.modified, true)
|
|
306
|
+
assert.match(result.code, /\[\.\.\.items\]\.length/)
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
test("Array.from().forEach() should NOT be transformed (handled by other transformer)", () => {
|
|
310
|
+
const input = `Array.from(items).forEach(item => console.log(item));`
|
|
311
|
+
|
|
312
|
+
const result = transform(input)
|
|
313
|
+
|
|
314
|
+
// Should be transformed by arrayFromForEachToForOf, not arrayFromToSpread
|
|
315
|
+
assert.strictEqual(result.modified, true)
|
|
316
|
+
assert.match(result.code, /for \(const item of items\)/)
|
|
317
|
+
assert.doesNotMatch(result.code, /\[\.\.\./)
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
test("Array.from() with mapping function should NOT be transformed", () => {
|
|
321
|
+
const input = `const doubled = Array.from(numbers, n => n * 2);`
|
|
322
|
+
|
|
323
|
+
const result = transform(input)
|
|
324
|
+
|
|
325
|
+
// Should not transform because there's a mapping function
|
|
326
|
+
assert.strictEqual(result.modified, false)
|
|
327
|
+
assert.match(result.code, /Array\.from\(numbers, n => n \* 2\)/)
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
test("Array.from() with thisArg should NOT be transformed", () => {
|
|
331
|
+
const input = `const result = Array.from(items, function(x) { return x * this.multiplier; }, context);`
|
|
332
|
+
|
|
333
|
+
const result = transform(input)
|
|
334
|
+
|
|
335
|
+
// Should not transform because there are 3 arguments
|
|
336
|
+
assert.strictEqual(result.modified, false)
|
|
337
|
+
assert.match(result.code, /Array\.from/)
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
test("Array.from() chained methods", () => {
|
|
341
|
+
const input = `const result = Array.from(set).map(x => x * 2).filter(x => x > 10);`
|
|
342
|
+
|
|
343
|
+
const result = transform(input)
|
|
344
|
+
|
|
345
|
+
assert.strictEqual(result.modified, true)
|
|
346
|
+
assert.match(result.code, /\[\.\.\.set\]\.map/)
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
test("Array.from() with complex iterable", () => {
|
|
350
|
+
const input = `const arr = Array.from(document.querySelectorAll('.item'));`
|
|
351
|
+
|
|
352
|
+
const result = transform(input)
|
|
353
|
+
|
|
354
|
+
assert.strictEqual(result.modified, true)
|
|
355
|
+
assert.match(result.code, /\[\.\.\.document\.querySelectorAll\('\.item'\)\]/)
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
test("Array.from() tracks line numbers", () => {
|
|
359
|
+
const input = `// Line 1
|
|
360
|
+
const result = Array.from(items).map(x => x * 2);`
|
|
361
|
+
|
|
362
|
+
const result = transform(input)
|
|
363
|
+
|
|
364
|
+
assert.strictEqual(result.modified, true)
|
|
365
|
+
assert.strictEqual(result.changes.length, 1)
|
|
366
|
+
assert.strictEqual(result.changes[0].type, "arrayFromToSpread")
|
|
367
|
+
assert.strictEqual(result.changes[0].line, 2)
|
|
368
|
+
})
|
|
369
|
+
})
|
|
370
|
+
|
|
234
371
|
describe("const and let", () => {
|
|
235
372
|
test("var to const when not reassigned", () => {
|
|
236
373
|
const input = `
|