esupgrade 2025.3.3 → 2025.4.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.
@@ -0,0 +1,18 @@
1
+ When writing code, you MUST ALWAYS follow the [naming-things](https://raw.githubusercontent.com/codingjoe/naming-things/refs/heads/main/README.md) guidelines.
2
+
3
+ All code must be fully tested with a 100% coverage. Unreachable code must be removed.
4
+
5
+ All transformers must be documented in the README.md.
6
+
7
+ Use class syntax for all object-oriented code.
8
+ Use named functions instead of anonymous functions whenever possible.
9
+ Use `#` for private methods.
10
+
11
+ Avoid overly complex functions. Break them into smaller functions if necessary.
12
+
13
+ Write docstrings with jsdoc type annotations for all functions, classes, and methods.
14
+ Docstrings should be written in present tense imperative mood.
15
+ Docstrings must describe the external behavior of the function, class, or method.
16
+ Docstrings should avoid redundant phrases like "This function" or "This method".
17
+ Class docstrings must not repeat the class name or start with a verb since they don't do anything themselves.
18
+ Avoid code comments unless they describe behavior of 3rd party code or complex algorithms.
package/README.md CHANGED
@@ -212,6 +212,47 @@ Supports:
212
212
  > [!NOTE]
213
213
  > Functions using `this`, `arguments`, or `super` are not converted to preserve semantics.
214
214
 
215
+ #### Constructor functions → [Classes][mdn-classes]
216
+
217
+ ```diff
218
+ -function Person(name, age) {
219
+ - this.name = name;
220
+ - this.age = age;
221
+ -}
222
+ -
223
+ -Person.prototype.greet = function() {
224
+ - return 'Hello, I am ' + this.name;
225
+ -};
226
+ -
227
+ -Person.prototype.getAge = function() {
228
+ - return this.age;
229
+ -};
230
+ +class Person {
231
+ + constructor(name, age) {
232
+ + this.name = name;
233
+ + this.age = age;
234
+ + }
235
+ +
236
+ + greet() {
237
+ + return 'Hello, I am ' + this.name;
238
+ + }
239
+ +
240
+ + getAge() {
241
+ + return this.age;
242
+ + }
243
+ +}
244
+ ```
245
+
246
+ > [!NOTE]
247
+ > Transforms constructor functions (both function declarations and variable declarations) that meet these criteria:
248
+ >
249
+ > - Function name starts with an uppercase letter
250
+ > - Constructor bodies are limited to simple statements (variable declarations and expression statements)
251
+ > - No control flow statements (`if`, `for`, `while`, `return`, `throw`, etc.) in constructor body
252
+ > - At least one prototype method is defined
253
+ > - Prototype methods must be function expressions (not arrow functions)
254
+ > - Prototype object literals with getters, setters, or computed properties are skipped
255
+
215
256
  <picture>
216
257
  <source media="(prefers-color-scheme: dark)" srcset="https://web-platform-dx.github.io/web-features/assets/img/baseline-newly-word-dark.svg">
217
258
  <source media="(prefers-color-scheme: light)" srcset="https://web-platform-dx.github.io/web-features/assets/img/baseline-newly-word.svg">
@@ -258,6 +299,7 @@ Furthermore, esupgrade supports JavaScript, TypeScript, and more, while lebab is
258
299
  [calver]: https://calver.org/
259
300
  [django-upgrade]: https://github.com/adamchainz/django-upgrade
260
301
  [mdn-arrow-functions]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
302
+ [mdn-classes]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
261
303
  [mdn-const]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
262
304
  [mdn-exponentiation]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Exponentiation
263
305
  [mdn-for-in]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
package/bin/esupgrade.js CHANGED
@@ -74,7 +74,8 @@ class FileProcessor {
74
74
  const result = workerResult.result
75
75
 
76
76
  if (result.modified) {
77
- if (options.check) {
77
+ // Report changes if check mode or if not writing (dry-run)
78
+ if (options.check || !options.write) {
78
79
  this.#reportChanges(filePath, result.changes)
79
80
  }
80
81
 
@@ -89,6 +90,7 @@ class FileProcessor {
89
90
 
90
91
  return { modified: true, changes: result.changes, error: false }
91
92
  } else {
93
+ // Show unmodified files unless in check-only mode
92
94
  if (!options.check) {
93
95
  console.log(` ${filePath}`)
94
96
  }
@@ -237,6 +239,14 @@ class CLIRunner {
237
239
  this.#reportSummary(results, options)
238
240
  }
239
241
 
242
+ #formatDetailedSummary(modifiedCount, allChanges, actionVerb) {
243
+ const transformTypes = new Set(allChanges.map((c) => c.type))
244
+ const typeCount = transformTypes.size
245
+ const totalChanges = allChanges.length
246
+
247
+ return `${modifiedCount} file${modifiedCount !== 1 ? "s" : ""} ${actionVerb} (${totalChanges} change${totalChanges !== 1 ? "s" : ""}, ${typeCount} type${typeCount !== 1 ? "s" : ""})`
248
+ }
249
+
240
250
  #reportSummary(results, options) {
241
251
  let modifiedCount = 0
242
252
  const allChanges = results.flatMap((result) =>
@@ -249,12 +259,12 @@ class CLIRunner {
249
259
 
250
260
  if (options.check) {
251
261
  if (modifiedCount > 0) {
252
- const transformTypes = new Set(allChanges.map((c) => c.type))
253
- const typeCount = transformTypes.size
254
- const totalChanges = allChanges.length
255
-
256
262
  console.log(
257
- `${modifiedCount} file${modifiedCount !== 1 ? "s" : ""} need${modifiedCount === 1 ? "s" : ""} upgrading (${totalChanges} change${totalChanges !== 1 ? "s" : ""}, ${typeCount} type${typeCount !== 1 ? "s" : ""})`,
263
+ this.#formatDetailedSummary(
264
+ modifiedCount,
265
+ allChanges,
266
+ `need${modifiedCount === 1 ? "s" : ""} upgrading`,
267
+ ),
258
268
  )
259
269
  if (options.write) {
260
270
  console.log("Changes have been written")
@@ -262,12 +272,22 @@ class CLIRunner {
262
272
  } else {
263
273
  console.log("All files are up to date")
264
274
  }
265
- } else {
275
+ } else if (options.write) {
276
+ // --write without --check
266
277
  if (modifiedCount > 0) {
267
278
  console.log(`✓ ${modifiedCount} file${modifiedCount !== 1 ? "s" : ""} upgraded`)
268
279
  } else {
269
280
  console.log("All files are up to date")
270
281
  }
282
+ } else {
283
+ // Dry-run mode (no --check, no --write)
284
+ if (modifiedCount > 0) {
285
+ console.log(
286
+ this.#formatDetailedSummary(modifiedCount, allChanges, "would be upgraded"),
287
+ )
288
+ } else {
289
+ console.log("All files are up to date")
290
+ }
271
291
  }
272
292
 
273
293
  // Errors take precedence over --check flag.
@@ -296,14 +316,11 @@ program
296
316
  .default("widely-available"),
297
317
  )
298
318
  .option("--check", "Report which files need upgrading and exit with code 1 if any do")
299
- .option(
300
- "--write",
301
- "Write changes to files (default: true unless only --check is specified)",
302
- )
319
+ .option("--write", "Write changes to files")
303
320
  .action(async (files, options) => {
304
321
  // Handle check/write options - they are not mutually exclusive
305
- // Default: write is true unless ONLY --check is specified (no --write)
306
- const shouldWrite = options.write !== undefined ? options.write : !options.check
322
+ // Default: write is false (read-only mode unless --write is specified)
323
+ const shouldWrite = options.write || false
307
324
  const shouldCheck = options.check || false
308
325
 
309
326
  const processingOptions = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "esupgrade",
3
- "version": "2025.3.3",
3
+ "version": "2025.4.0",
4
4
  "description": "Auto-upgrade your JavaScript syntax",
5
5
  "type": "module",
6
6
  "main": "src/index.js",