esupgrade 2025.3.0 → 2025.3.2
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 -0
- package/bin/esupgrade.js +2 -2
- 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 +150 -148
- package/tests/widelyAvailable.test.js +1074 -1399
package/README.md
CHANGED
|
@@ -40,6 +40,12 @@ pre-commit run esupgrade --all-files
|
|
|
40
40
|
npx esupgrade --help
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
<picture>
|
|
44
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://web-platform-dx.github.io/web-features/assets/img/baseline-wordmark-dark.svg">
|
|
45
|
+
<source media="(prefers-color-scheme: light)" srcset="https://web-platform-dx.github.io/web-features/assets/img/baseline-wordmark.svg">
|
|
46
|
+
<img alt="Baseline: widely available" src="https://web-platform-dx.github.io/web-features/assets/img/baseline-wordmark.svg" height="32" align="right">
|
|
47
|
+
</picture>
|
|
48
|
+
|
|
43
49
|
## Browser Support & Baseline
|
|
44
50
|
|
|
45
51
|
All transformations are based on [Web Platform Baseline][baseline] features. Baseline tracks which web platform features are safe to use across browsers.
|
|
@@ -229,7 +235,28 @@ Supports:
|
|
|
229
235
|
+});
|
|
230
236
|
```
|
|
231
237
|
|
|
238
|
+
## Versioning
|
|
239
|
+
|
|
240
|
+
esupgrade uses the [calver] `YYYY.MINOR.PATCH` versioning scheme.
|
|
241
|
+
|
|
242
|
+
The year indicates the baseline version. New transformations are added in minor releases, while patches are reserved for bug fixes.
|
|
243
|
+
|
|
244
|
+
## Related Projects
|
|
245
|
+
|
|
246
|
+
Thanks to these projects for inspiring esupgrade:
|
|
247
|
+
|
|
248
|
+
- @asottile's [pyupgrade] for Python
|
|
249
|
+
- @adamchainz' [django-upgrade] for Django
|
|
250
|
+
|
|
251
|
+
### Distinction
|
|
252
|
+
|
|
253
|
+
lebab is a similar project that focuses on ECMAScript 6+ transformations without considering browser support.
|
|
254
|
+
esupgrade is distinct in that it applies transformations that are safe based on Baseline browser support.
|
|
255
|
+
Furthermore, esupgrade supports JavaScript, TypeScript, and more, while lebab is limited to JavaScript.
|
|
256
|
+
|
|
232
257
|
[baseline]: https://web.dev/baseline/
|
|
258
|
+
[calver]: https://calver.org/
|
|
259
|
+
[django-upgrade]: https://github.com/adamchainz/django-upgrade
|
|
233
260
|
[mdn-arrow-functions]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
|
|
234
261
|
[mdn-const]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
|
|
235
262
|
[mdn-exponentiation]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Exponentiation
|
|
@@ -240,3 +267,4 @@ Supports:
|
|
|
240
267
|
[mdn-spread]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
|
|
241
268
|
[mdn-template-literals]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
|
|
242
269
|
[pre-commit]: https://pre-commit.com/
|
|
270
|
+
[pyupgrade]: https://github.com/asottile/pyupgrade
|
package/bin/esupgrade.js
CHANGED
|
@@ -176,7 +176,7 @@ function processFiles(patterns, options) {
|
|
|
176
176
|
const totalChanges = allChanges.length
|
|
177
177
|
|
|
178
178
|
console.log(
|
|
179
|
-
`${modifiedCount} file
|
|
179
|
+
`${modifiedCount} file${modifiedCount !== 1 ? "s" : ""} need${modifiedCount === 1 ? "s" : ""} upgrading (${totalChanges} change${totalChanges !== 1 ? "s" : ""}, ${typeCount} type${typeCount !== 1 ? "s" : ""})`,
|
|
180
180
|
)
|
|
181
181
|
if (options.write) {
|
|
182
182
|
console.log("Changes have been written")
|
|
@@ -187,7 +187,7 @@ function processFiles(patterns, options) {
|
|
|
187
187
|
} else {
|
|
188
188
|
console.log("")
|
|
189
189
|
if (modifiedCount > 0) {
|
|
190
|
-
console.log(`✓ ${modifiedCount} file
|
|
190
|
+
console.log(`✓ ${modifiedCount} file${modifiedCount !== 1 ? "s" : ""} upgraded`)
|
|
191
191
|
} else {
|
|
192
192
|
console.log("All files are up to date")
|
|
193
193
|
}
|
package/package.json
CHANGED
package/src/widelyAvailable.js
CHANGED
|
@@ -68,17 +68,32 @@ export function concatToTemplateLiteral(j, root) {
|
|
|
68
68
|
return false
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
// Helper to get the raw value of a string literal (preserving escape sequences)
|
|
72
|
+
const getRawStringValue = (node) => {
|
|
73
|
+
// In string literals, backslashes are used for escape sequences
|
|
74
|
+
// In template literals, backslashes in the raw value also need escaping
|
|
75
|
+
// So we need to double the backslashes: \\ -> \\\\
|
|
76
|
+
// Note: node.extra.rawValue is always defined for string literals with the current parser
|
|
77
|
+
return node.extra.rawValue.replace(/\\/g, "\\\\")
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const addStringPart = (stringNode) => {
|
|
81
|
+
// Store both the raw and cooked values
|
|
82
|
+
const rawValue = getRawStringValue(stringNode)
|
|
83
|
+
const cookedValue = stringNode.value
|
|
84
|
+
|
|
72
85
|
if (parts.length === 0 || expressions.length >= parts.length) {
|
|
73
|
-
parts.push(
|
|
86
|
+
parts.push({ raw: rawValue, cooked: cookedValue })
|
|
74
87
|
} else {
|
|
75
|
-
parts[parts.length - 1]
|
|
88
|
+
const lastPart = parts[parts.length - 1]
|
|
89
|
+
lastPart.raw += rawValue
|
|
90
|
+
lastPart.cooked += cookedValue
|
|
76
91
|
}
|
|
77
92
|
}
|
|
78
93
|
|
|
79
94
|
const addExpression = (expr) => {
|
|
80
95
|
if (parts.length === 0) {
|
|
81
|
-
parts.push("")
|
|
96
|
+
parts.push({ raw: "", cooked: "" })
|
|
82
97
|
}
|
|
83
98
|
expressions.push(expr)
|
|
84
99
|
}
|
|
@@ -103,8 +118,8 @@ export function concatToTemplateLiteral(j, root) {
|
|
|
103
118
|
// Left is also a + expression - recurse
|
|
104
119
|
flatten(node.left, stringContext)
|
|
105
120
|
} else if (isStringLiteral(node.left)) {
|
|
106
|
-
// Left is a string literal
|
|
107
|
-
addStringPart(node.left
|
|
121
|
+
// Left is a string literal - use raw value to preserve escape sequences
|
|
122
|
+
addStringPart(node.left)
|
|
108
123
|
} else {
|
|
109
124
|
// Left is some other expression
|
|
110
125
|
addExpression(node.left)
|
|
@@ -121,8 +136,8 @@ export function concatToTemplateLiteral(j, root) {
|
|
|
121
136
|
flatten(node.right, rightInStringContext)
|
|
122
137
|
}
|
|
123
138
|
} else if (isStringLiteral(node.right)) {
|
|
124
|
-
// Right is a string literal
|
|
125
|
-
addStringPart(node.right
|
|
139
|
+
// Right is a string literal - use raw value to preserve escape sequences
|
|
140
|
+
addStringPart(node.right)
|
|
126
141
|
} else {
|
|
127
142
|
// Right is some other expression
|
|
128
143
|
addExpression(node.right)
|
|
@@ -135,12 +150,15 @@ export function concatToTemplateLiteral(j, root) {
|
|
|
135
150
|
|
|
136
151
|
// Ensure we have the right number of quasis (one more than expressions)
|
|
137
152
|
while (parts.length <= expressions.length) {
|
|
138
|
-
parts.push("")
|
|
153
|
+
parts.push({ raw: "", cooked: "" })
|
|
139
154
|
}
|
|
140
155
|
|
|
141
156
|
// Create template literal
|
|
142
157
|
const quasis = parts.map((part, i) =>
|
|
143
|
-
j.templateElement(
|
|
158
|
+
j.templateElement(
|
|
159
|
+
{ raw: part.raw, cooked: part.cooked },
|
|
160
|
+
i === parts.length - 1,
|
|
161
|
+
),
|
|
144
162
|
)
|
|
145
163
|
|
|
146
164
|
const templateLiteral = j.templateLiteral(quasis, expressions)
|
|
@@ -913,62 +931,6 @@ export function anonymousFunctionToArrow(j, root) {
|
|
|
913
931
|
return found
|
|
914
932
|
}
|
|
915
933
|
|
|
916
|
-
// Helper to check if a node or its descendants use 'super'
|
|
917
|
-
const usesSuper = (node) => {
|
|
918
|
-
let found = false
|
|
919
|
-
|
|
920
|
-
const visit = (n) => {
|
|
921
|
-
if (!n || typeof n !== "object" || found) return
|
|
922
|
-
|
|
923
|
-
// If we encounter a nested function, don't traverse into it
|
|
924
|
-
// as it has its own 'super' binding context
|
|
925
|
-
if (n.type === "FunctionExpression" || n.type === "FunctionDeclaration") {
|
|
926
|
-
return
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
// Check if this is a 'super' node
|
|
930
|
-
if (n.type === "Super") {
|
|
931
|
-
found = true
|
|
932
|
-
return
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
// Traverse all child nodes
|
|
936
|
-
for (const key in n) {
|
|
937
|
-
if (
|
|
938
|
-
key === "loc" ||
|
|
939
|
-
key === "start" ||
|
|
940
|
-
key === "end" ||
|
|
941
|
-
key === "tokens" ||
|
|
942
|
-
key === "comments" ||
|
|
943
|
-
key === "type"
|
|
944
|
-
) {
|
|
945
|
-
continue
|
|
946
|
-
}
|
|
947
|
-
const value = n[key]
|
|
948
|
-
if (Array.isArray(value)) {
|
|
949
|
-
for (const item of value) {
|
|
950
|
-
visit(item)
|
|
951
|
-
if (found) return
|
|
952
|
-
}
|
|
953
|
-
} else if (value && typeof value === "object") {
|
|
954
|
-
visit(value)
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
// Start visiting from the function body's child nodes
|
|
960
|
-
// Don't check the body node itself, check its contents
|
|
961
|
-
// Note: FunctionExpression.body is always a BlockStatement
|
|
962
|
-
if (node.type === "BlockStatement" && node.body) {
|
|
963
|
-
for (const statement of node.body) {
|
|
964
|
-
visit(statement)
|
|
965
|
-
if (found) break
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
return found
|
|
970
|
-
}
|
|
971
|
-
|
|
972
934
|
root
|
|
973
935
|
.find(j.FunctionExpression)
|
|
974
936
|
.filter((path) => {
|
|
@@ -996,10 +958,8 @@ export function anonymousFunctionToArrow(j, root) {
|
|
|
996
958
|
return false
|
|
997
959
|
}
|
|
998
960
|
|
|
999
|
-
//
|
|
1000
|
-
|
|
1001
|
-
return false
|
|
1002
|
-
}
|
|
961
|
+
// Note: We don't need to check for 'super' because using super in a
|
|
962
|
+
// function expression is a syntax error and will never parse successfully
|
|
1003
963
|
|
|
1004
964
|
return true
|
|
1005
965
|
})
|