esupgrade 2025.19.0 → 2025.20.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.
Files changed (143) hide show
  1. package/.gitattributes +0 -1
  2. package/.github/agents/superjoe.agent.md +1 -2
  3. package/.pre-commit-config.yaml +2 -2
  4. package/AGENTS.md +10 -0
  5. package/CONTRIBUTING.md +5 -0
  6. package/README.md +105 -188
  7. package/SKILL.md +14 -0
  8. package/bin/esupgrade.js +3 -11
  9. package/package.json +1 -1
  10. package/src/index.js +1 -7
  11. package/src/newlyAvailable/promiseTry.js +1 -0
  12. package/src/widelyAvailable/anonymousFunctionToArrow.js +1 -0
  13. package/src/widelyAvailable/argumentsToRestParameters.js +2 -2
  14. package/src/widelyAvailable/arrayConcatToSpread.js +1 -0
  15. package/src/widelyAvailable/arrayFilterToFind.js +1 -0
  16. package/src/widelyAvailable/arrayFromForEachToForOf.js +1 -0
  17. package/src/widelyAvailable/arrayFromToSpread.js +1 -0
  18. package/src/widelyAvailable/arraySliceToSpread.js +1 -0
  19. package/src/widelyAvailable/compoundAssignment.js +53 -0
  20. package/src/widelyAvailable/concatToTemplateLiteral.js +2 -1
  21. package/src/widelyAvailable/consoleLogToInfo.js +1 -0
  22. package/src/widelyAvailable/constructorToClass.js +239 -172
  23. package/src/widelyAvailable/defaultParameterValues.js +1 -0
  24. package/src/widelyAvailable/forLoopToForOf.js +1 -0
  25. package/src/widelyAvailable/globalContextToGlobalThis.js +1 -0
  26. package/src/widelyAvailable/indexOfToIncludes.js +1 -0
  27. package/src/widelyAvailable/indexOfToStartsWith.js +1 -0
  28. package/src/widelyAvailable/iterableForEachToForOf.js +1 -0
  29. package/src/widelyAvailable/lastIndexOfToEndsWith.js +1 -0
  30. package/src/widelyAvailable/logicalAssignment.js +83 -0
  31. package/src/widelyAvailable/mathPowToExponentiation.js +1 -0
  32. package/src/widelyAvailable/namedArrowFunctionToNamedFunction.js +1 -0
  33. package/src/widelyAvailable/negativeIndexToAt.js +76 -0
  34. package/src/widelyAvailable/nullishCoalescingOperator.js +1 -0
  35. package/src/widelyAvailable/numericSeparators.js +67 -0
  36. package/src/widelyAvailable/objectAssignToSpread.js +1 -0
  37. package/src/widelyAvailable/objectHasOwn.js +72 -0
  38. package/src/widelyAvailable/objectKeysForEachToEntries.js +1 -0
  39. package/src/widelyAvailable/objectKeysMapToValues.js +1 -0
  40. package/src/widelyAvailable/objectPropertyExtractionToDestructuring.js +1 -0
  41. package/src/widelyAvailable/optionalChaining.js +1 -0
  42. package/src/widelyAvailable/promiseToAsyncAwait.js +1 -0
  43. package/src/widelyAvailable/removeUseStrictFromModules.js +1 -0
  44. package/src/widelyAvailable/replaceAll.js +171 -0
  45. package/src/widelyAvailable/substrToSlice.js +1 -0
  46. package/src/widelyAvailable/substringToStartsWith.js +1 -0
  47. package/src/widelyAvailable/varToLetOrConst.js +1 -0
  48. package/src/widelyAvailable.js +6 -0
  49. package/src/worker.js +2 -2
  50. package/tests/baseline.test.js +51 -0
  51. package/tests/cli.test.js +0 -61
  52. package/tests/general.test.js +5 -6
  53. package/tests/widelyAvailable/compound-assignment.test.js +89 -0
  54. package/tests/widelyAvailable/constructor-to-class.test.js +331 -0
  55. package/tests/widelyAvailable/default-parameter-values.test.js +1 -1
  56. package/tests/widelyAvailable/for-loop-to-for-of.test.js +2 -1
  57. package/tests/widelyAvailable/logical-assignment.test.js +134 -0
  58. package/tests/widelyAvailable/negative-index-to-at.test.js +95 -0
  59. package/tests/widelyAvailable/numeric-separators.test.js +62 -0
  60. package/tests/widelyAvailable/object-has-own.test.js +74 -0
  61. package/tests/widelyAvailable/replace-all.test.js +156 -0
  62. package/src/jQuery/HTMLInputElement.json +0 -389
  63. package/src/jQuery/addClassToClassList.js +0 -49
  64. package/src/jQuery/afterToAfter.js +0 -54
  65. package/src/jQuery/appendToAppend.js +0 -55
  66. package/src/jQuery/attrToGetSetRemove.js +0 -52
  67. package/src/jQuery/beforeToBefore.js +0 -47
  68. package/src/jQuery/childrenToChildrenArray.js +0 -48
  69. package/src/jQuery/clickToAddEventListener.js +0 -52
  70. package/src/jQuery/closestToClosest.js +0 -43
  71. package/src/jQuery/cssToStyleAndComputed.js +0 -120
  72. package/src/jQuery/eachToForOf.js +0 -92
  73. package/src/jQuery/emptyToReplaceChildren.js +0 -33
  74. package/src/jQuery/findToQuerySelectorAll.js +0 -57
  75. package/src/jQuery/hasClassToClassListContains.js +0 -36
  76. package/src/jQuery/htmlToInnerHTML.js +0 -61
  77. package/src/jQuery/idSelectorToGetElementById.js +0 -54
  78. package/src/jQuery/inArrayToIncludes.js +0 -77
  79. package/src/jQuery/nextToNextElementSibling.js +0 -78
  80. package/src/jQuery/offToRemoveEventListener.js +0 -36
  81. package/src/jQuery/onToAddEventListener.js +0 -51
  82. package/src/jQuery/parentToParentElement.js +0 -60
  83. package/src/jQuery/prependToPrepend.js +0 -54
  84. package/src/jQuery/prevToPreviousElementSibling.js +0 -76
  85. package/src/jQuery/propToDirectProperty.js +0 -175
  86. package/src/jQuery/readyToDOMContentLoaded.js +0 -78
  87. package/src/jQuery/removeClassToClassList.js +0 -45
  88. package/src/jQuery/removeToRemove.js +0 -49
  89. package/src/jQuery/selectorToQuerySelectorAll.js +0 -68
  90. package/src/jQuery/showHideToStyleDisplay.js +0 -56
  91. package/src/jQuery/siblingsToSiblingsArray.js +0 -70
  92. package/src/jQuery/staticEachToForEach.js +0 -42
  93. package/src/jQuery/staticGrepToFilter.js +0 -36
  94. package/src/jQuery/staticMapToMap.js +0 -33
  95. package/src/jQuery/textToTextContent.js +0 -49
  96. package/src/jQuery/toggleClassToClassList.js +0 -36
  97. package/src/jQuery/triggerToDispatchEvent.js +0 -65
  98. package/src/jQuery/trimToStringTrim.js +0 -30
  99. package/src/jQuery/unwrapJQueryIdentifier.js +0 -51
  100. package/src/jQuery/utils.js +0 -384
  101. package/src/jQuery/valToValue.js +0 -40
  102. package/src/jQuery/widthHeightToClient.js +0 -49
  103. package/src/jQuery.js +0 -46
  104. package/tests/jQuery/addClassToClassList.test.js +0 -34
  105. package/tests/jQuery/afterToAfter.test.js +0 -38
  106. package/tests/jQuery/appendToAppend.test.js +0 -44
  107. package/tests/jQuery/attrToGetSetRemove.test.js +0 -44
  108. package/tests/jQuery/beforeToBefore.test.js +0 -44
  109. package/tests/jQuery/childrenToChildrenArray.test.js +0 -32
  110. package/tests/jQuery/clickToAddEventListener.test.js +0 -44
  111. package/tests/jQuery/closestToClosest.test.js +0 -38
  112. package/tests/jQuery/cssToStyleAndComputed.test.js +0 -71
  113. package/tests/jQuery/eachToForOf.test.js +0 -57
  114. package/tests/jQuery/emptyToReplaceChildren.test.js +0 -26
  115. package/tests/jQuery/findToQuerySelectorAll.test.js +0 -47
  116. package/tests/jQuery/hasClassToClassListContains.test.js +0 -41
  117. package/tests/jQuery/htmlToInnerHTML.test.js +0 -38
  118. package/tests/jQuery/idSelectorToGetElementById.test.js +0 -31
  119. package/tests/jQuery/inArrayToIncludes.test.js +0 -69
  120. package/tests/jQuery/nextToNextElementSibling.test.js +0 -37
  121. package/tests/jQuery/offToRemoveEventListener.test.js +0 -43
  122. package/tests/jQuery/onToAddEventListener.test.js +0 -41
  123. package/tests/jQuery/parentToParentElement.test.js +0 -34
  124. package/tests/jQuery/prependToPrepend.test.js +0 -32
  125. package/tests/jQuery/prevToPreviousElementSibling.test.js +0 -37
  126. package/tests/jQuery/propToDirectProperty.test.js +0 -65
  127. package/tests/jQuery/readyToDOMContentLoaded.test.js +0 -40
  128. package/tests/jQuery/removeClassToClassList.test.js +0 -47
  129. package/tests/jQuery/removeToRemove.test.js +0 -26
  130. package/tests/jQuery/selectorToQuerySelectorAll.test.js +0 -36
  131. package/tests/jQuery/showHideToStyleDisplay.test.js +0 -38
  132. package/tests/jQuery/siblingsToSiblingsArray.test.js +0 -32
  133. package/tests/jQuery/staticEachToForEach.test.js +0 -80
  134. package/tests/jQuery/staticGrepToFilter.test.js +0 -50
  135. package/tests/jQuery/staticMapToMap.test.js +0 -56
  136. package/tests/jQuery/textToTextContent.test.js +0 -38
  137. package/tests/jQuery/toggleClassToClassList.test.js +0 -38
  138. package/tests/jQuery/triggerToDispatchEvent.test.js +0 -45
  139. package/tests/jQuery/trimToStringTrim.test.js +0 -32
  140. package/tests/jQuery/unwrapJQueryIdentifier.test.js +0 -26
  141. package/tests/jQuery/utils.test.js +0 -283
  142. package/tests/jQuery/valToValue.test.js +0 -38
  143. package/tests/jQuery/widthHeightToClient.test.js +0 -38
package/.gitattributes CHANGED
@@ -1,2 +1 @@
1
1
  package-lock.json linguist-generated
2
- src/jQuery/HTMLInputElement.json linguist-generated
@@ -5,12 +5,12 @@ name: SuperJoe
5
5
  description: CodingJoe's digital clone following his coding guidelines and best practices.
6
6
  ---
7
7
 
8
-
9
8
  # SuperJoe
10
9
 
11
10
  ## Planning
12
11
 
13
12
  You MUST ALWAYS follow the `naming-things` guidelines. Use the following command to access the guidelines:
13
+
14
14
  ```console
15
15
  curl -sSL https://raw.githubusercontent.com/codingjoe/naming-things/refs/heads/main/README.md | head -n 500
16
16
  ```
@@ -44,7 +44,6 @@ Avoid functions or other code inside functions.
44
44
  Avoid if-statements in favor of switch/match-statements or polymorphism.
45
45
  Do not assign names to objects which are returned in the next line.
46
46
 
47
-
48
47
  ## Python
49
48
 
50
49
  Follow PEP 8 guidelines for code style.
@@ -18,10 +18,10 @@ repos:
18
18
  hooks:
19
19
  - id: mdformat
20
20
  additional_dependencies:
21
+ - mdformat-front-matters
21
22
  - mdformat-footnote
22
23
  - mdformat-gfm
23
24
  - mdformat-gfm-alerts
24
- exclude: '.github/agents/'
25
25
  - repo: https://github.com/google/yamlfmt
26
26
  rev: v0.21.0
27
27
  hooks:
@@ -32,7 +32,7 @@ repos:
32
32
  - id: write-good
33
33
  args: [--no-passive]
34
34
  - repo: https://github.com/pre-commit/mirrors-eslint
35
- rev: v10.4.0
35
+ rev: v10.4.1
36
36
  hooks:
37
37
  - id: eslint
38
38
  args: ["--fix"]
package/AGENTS.md CHANGED
@@ -31,3 +31,13 @@ console.log('modified:', res.modified);
31
31
  console.log('code:\n' + res.code);
32
32
  EOF
33
33
  ```
34
+
35
+ ## Writing Transformers
36
+
37
+ Transformers must only apply to types that are statically verifiable. Use `NodeTest` from
38
+ `src/types.js` to guard transformations:
39
+
40
+ - `new NodeTest(node).isIterable()` — array literals, `new Array()`, `Array.from()`, and `Array.of()`. Use this when the transformation is array-specific (e.g., index access, `.at()`).
41
+ - `new NodeTest(node).hasIndexOfAndIncludes()` — arrays and strings (includes string literals and array method chains like `.map()`, `.filter()`). Use this when the transformation applies to both arrays and strings.
42
+
43
+ Never apply a transformation based solely on structural shape (e.g., a `.length` property or bracket access) without first verifying the receiver is a known type. An unknown identifier such as `arr` cannot be assumed to be an array and must not be transformed.
package/CONTRIBUTING.md CHANGED
@@ -24,3 +24,8 @@ uvx pre-commit run --all-files
24
24
  ## Naming conversions
25
25
 
26
26
  When writing code, please follow the [naming guidelines](https://raw.githubusercontent.com/codingjoe/naming-things/refs/heads/main/README.md).
27
+
28
+ ## Baselines release dates
29
+
30
+ The `baselineDate` can be found on
31
+ https://github.com/web-platform-dx/web-features named `baseline_low_date`.
package/README.md CHANGED
@@ -13,7 +13,7 @@ Keeping your JavaScript and TypeScript code up to date with full browser compati
13
13
  ## Usage
14
14
 
15
15
  esupgrade is safe and meant to be used automatically on your codebase.
16
- We recommend integrating it into your development workflow using [pre-commit].
16
+ We recommend integrating it into your development workflow using [pre-commit] or [husky].
17
17
 
18
18
  To try it out on a repository without writing changes, run:
19
19
 
@@ -40,6 +40,15 @@ repos:
40
40
  pre-commit run esupgrade --all-files
41
41
  ```
42
42
 
43
+ ### Husky
44
+
45
+ Assuming Husky is already initialized and `.husky/pre-commit` already contains `set -e`, append:
46
+
47
+ ```bash
48
+ echo "git diff --cached --name-only --diff-filter=ACMR -z -- '*.js' '*.jsx' '*.ts' '*.tsx' '*.mjs' '*.cjs' | xargs -0 sh -c 'test \"\$#\" -eq 0 && exit 0; npx esupgrade -- \"\$@\"' sh" >> .husky/pre-commit
49
+ echo "git diff --cached --name-only --diff-filter=ACMR -z -- '*.js' '*.jsx' '*.ts' '*.tsx' '*.mjs' '*.cjs' | xargs -0 sh -c 'test \"\$#\" -eq 0 && exit 0; git add -- \"\$@\"' sh" >> .husky/pre-commit
50
+ ```
51
+
43
52
  ### CLI
44
53
 
45
54
  ```bash
@@ -215,6 +224,18 @@ Supports:
215
224
  +const withItem = [...array, item];
216
225
  ```
217
226
 
227
+ #### `1000000` → [Numeric separators][mdn-numeric-separators]
228
+
229
+ ```diff
230
+ -const budget = 1000000;
231
+ -const maxUsers = 1000000n;
232
+ +const budget = 1_000_000;
233
+ +const maxUsers = 1_000_000n;
234
+ ```
235
+
236
+ esupgrade transforms decimal integer and bigint literals with at least five digits.
237
+ Fractional, exponential, hexadecimal, octal, binary, and already formatted literals are left unchanged.
238
+
218
239
  #### `Array.slice(0)` → [Array spread [...]][mdn-spread]
219
240
 
220
241
  ```diff
@@ -231,9 +252,16 @@ Supports:
231
252
  +const first = [1, 2, 3].find(n => n > 1);
232
253
  ```
233
254
 
234
- Transforms `filter(predicate)[0]` to the more explicit and performant `find(predicate)`, which stops at the first match instead of filtering the entire array.
255
+ Transformations are limited to when the receiver can be verified as an array (array literals, `new Array()`, or known array method chains) and `filter()` is called with one argument.
235
256
 
236
- Transformations are limited to when the receiver can be verified as an array (array literals, `new Array()`, or known array method chains) and `filter()` is called with exactly one argument.
257
+ #### `arr[arr.length - n]` [`Array.at()`][mdn-at]
258
+
259
+ ```diff
260
+ -const last = arr[arr.length - 1];
261
+ -const item = arr[arr.length - n];
262
+ +const last = arr.at(-1);
263
+ +const item = arr.at(-n);
264
+ ```
237
265
 
238
266
  #### `Math.pow()` → [Exponentiation operator \*\*][mdn-exponentiation]
239
267
 
@@ -244,6 +272,31 @@ Transformations are limited to when the receiver can be verified as an array (ar
244
272
  +const area = Math.PI * radius ** 2;
245
273
  ```
246
274
 
275
+ #### Verbose arithmetic assignments → [Compound assignment operators (+=, -=, …)][mdn-compound-assignment]
276
+
277
+ ```diff
278
+ -x = x + y
279
+ +x += y
280
+ -x = x - y
281
+ +x -= y
282
+ -x = x * y
283
+ +x *= y
284
+ -x = x / y
285
+ +x /= y
286
+ -x = x % y
287
+ +x %= y
288
+ -x = x ** y
289
+ +x **= y
290
+ ```
291
+
292
+ > [!NOTE]
293
+ > When the assignment target is a member expression with potential side effects
294
+ > (e.g., `getObj().prop = getObj().prop + y` or `obj[f()] = obj[f()] + y`), the
295
+ > transformation evaluates the target expression once instead of twice.
296
+ > If `getObj()` or `f()` have observable side effects or return different values
297
+ > on each call, `getObj().prop += y` may produce different results than the
298
+ > original `getObj().prop = getObj().prop + y`.
299
+
247
300
  #### Named function assignments → [Function declarations][mdn-functions]
248
301
 
249
302
  ```diff
@@ -336,6 +389,7 @@ Transforms constructor functions (both function declarations and variable declar
336
389
 
337
390
  - Function name starts with an uppercase letter
338
391
  - At least one prototype method is defined
392
+ - Prototype methods are matched only to constructors declared in the current or an ancestor lexical scope (never sibling or child scopes)
339
393
  - Prototype methods using `this` in arrow functions are skipped
340
394
  - Prototype object literals with getters, setters, or computed properties are skipped
341
395
 
@@ -397,6 +451,32 @@ ES6 modules are automatically in strict mode, making explicit `'use strict'` dir
397
451
  +const result = obj.prop ?? 0;
398
452
  ```
399
453
 
454
+ #### Logical assignment patterns → [Logical assignment operators (??=, ||=, &&=)][mdn-logical-assignment]
455
+
456
+ ```diff
457
+ -x = x ?? y
458
+ +x ??= y
459
+ -x = x || y
460
+ +x ||= y
461
+ -x = x && y
462
+ +x &&= y
463
+ ```
464
+
465
+ ```diff
466
+ -if (x === null || x === undefined) x = y
467
+ +x ??= y
468
+ ```
469
+
470
+ > [!NOTE]
471
+ > For member expression targets (e.g., `obj.prop ||= y`), the original
472
+ > unconditional assignment always invokes property setters, while the logical
473
+ > assignment operator skips the setter when the condition is not met. This
474
+ > generally improves performance by avoiding unnecessary setter calls.
475
+ >
476
+ > Similarly, for `if (obj.prop === null || obj.prop === undefined) obj.prop = y`,
477
+ > the transformation to `obj.prop ??= y` reads `obj.prop` once instead of twice
478
+ > before assigning, which improves performance for getter-backed properties.
479
+
400
480
  #### `indexOf()` → [includes()][mdn-includes]
401
481
 
402
482
  ```diff
@@ -437,6 +517,15 @@ Transforms the deprecated `substr()` method to `slice()`:
437
517
 
438
518
  Transformations are limited to when the receiver can be verified as a string (string literals, template literals, or string method chains).
439
519
 
520
+ #### `split().join()` / `replace(/literal/g)` → [String.replaceAll()][mdn-replaceall]
521
+
522
+ ```diff
523
+ -const value = "a,b,c".split(",").join(".");
524
+ -const label = "foo".replace(/o/g, "a");
525
+ +const value = "a,b,c".replaceAll(",", ".");
526
+ +const label = "foo".replaceAll("o", "a");
527
+ ```
528
+
440
529
  #### `Object.keys().forEach()` → [Object.entries()][mdn-object-entries]
441
530
 
442
531
  ```diff
@@ -449,8 +538,6 @@ Transformations are limited to when the receiver can be verified as a string (st
449
538
  +});
450
539
  ```
451
540
 
452
- Transforms Object.keys() iteration patterns where the value is accessed from the same object into Object.entries() with array destructuring. This eliminates duplicate property lookups and makes the code more concise.
453
-
454
541
  Transforms when:
455
542
 
456
543
  - The callback has one parameter (the key)
@@ -464,11 +551,9 @@ Transforms when:
464
551
  +Object.values(obj)
465
552
  ```
466
553
 
467
- Transforms `Object.keys(obj).map(key => obj[key])` patterns to use `Object.values()` directly. This eliminates redundant property lookups and expresses intent more clearly.
468
-
469
554
  Transforms when:
470
555
 
471
- - The callback has exactly one parameter (the key)
556
+ - The callback has one parameter (the key)
472
557
  - The callback body returns `obj[key]` (expression or block with a single return statement)
473
558
  - The object being accessed matches the object passed to Object.keys()
474
559
 
@@ -628,187 +713,13 @@ These transformations are mainly to harden code for future releases and should b
628
713
  +});
629
714
  ```
630
715
 
631
- <picture>
632
- <source media="(prefers-color-scheme: dark)" srcset="https://jquery.com/wp-content/themes/jquery/images/logo-jquery@2x.png">
633
- <source media="(prefers-color-scheme: light)" srcset="https://upload.wikimedia.org/wikipedia/commons/f/fd/JQuery-Logo.svg">
634
- <img alt="jQuery logo" src="https://upload.wikimedia.org/wikipedia/commons/f/fd/JQuery-Logo.svg" height="32" align="right">
635
- </picture>
636
-
637
- ## jQuery
638
-
639
- esupgrade can transform common [jQuery] patterns to modern, standards-based DOM APIs via the `--jQuery` argument.
640
- For further reading, we recommend: [You Might Not Need jQuery](https://youmightnotneedjquery.com/).
641
-
642
- ```bash
643
- npx esupgrade --jQuery <files>
644
- ```
645
-
646
- > [!WARNING]
647
- > jQuery transformers are experimental and may produce unsafe code in complex scenarios.
648
- > Review all changes before applying them to your codebase.
649
-
650
- ### Selection
651
-
652
- #### `$(selector)` → `document.querySelectorAll()` / `document.getElementById()`
653
-
654
- ```diff
655
- -$('.foo')
656
- - $('#bar')
657
- +document.querySelectorAll('.foo')
658
- +document.getElementById('bar')
659
- ```
660
-
661
- Skipped when jQuery chaining is present (e.g. `$('.x').css(...).addClass(...)`).
662
-
663
- ### Class and Attributes
664
-
665
- #### Class helpers
666
-
667
- ```diff
668
- -$(el).addClass('a b');
669
- -$(el).removeClass('a');
670
- -$(el).toggleClass('x');
671
- -$(el).hasClass('x');
672
- +el.classList.add('a', 'b');
673
- +el.classList.remove('a');
674
- +el.classList.toggle('x');
675
- +el.classList.contains('x');
676
- ```
677
-
678
- #### Attribute helpers
679
-
680
- ```diff
681
- -$(el).attr('data');
682
- -$(el).attr('data', v);
683
- -$(el).removeAttr('disabled');
684
- +el.getAttribute('data');
685
- +el.setAttribute('data', v);
686
- +el.removeAttribute('disabled');
687
- ```
688
-
689
- ### Content and Value
690
-
691
- #### Text and HTML accessors
692
-
693
- ```diff
694
- -const text = $(el).text();
695
- -$(el).text('new text');
696
- -const html = $(el).html();
697
- -$(el).html('<b>bold</b>');
698
- +const text = el.textContent;
699
- +el.textContent = 'new text';
700
- +const html = el.innerHTML;
701
- +el.innerHTML = '<b>bold</b>';
702
- ```
703
-
704
- #### Value accessors
705
-
706
- ```diff
707
- -const val = $(el).val();
708
- -$(el).val('new value');
709
- +const val = el.value;
710
- +el.value = 'new value';
711
- ```
712
-
713
- ### DOM Manipulation
714
-
715
- #### Insertion and Removal
716
-
717
- ```diff
718
- -$(parent).append(child);
719
- -$(parent).prepend(child);
720
- -$(el).before(node);
721
- -$(el).after(node);
722
- -$(el).remove();
723
- -$(el).empty();
724
- +parent.append(child);
725
- +parent.prepend(child);
726
- +el.before(node);
727
- +el.after(node);
728
- +el.remove();
729
- +el.replaceChildren();
730
- ```
731
-
732
- ### Events
733
-
734
- #### Event Listeners
735
-
736
- ```diff
737
- -$(el).on('click', handler);
738
- -$(el).off('click', handler);
739
- -$(el).click(handler);
740
- +el.addEventListener('click', handler);
741
- +el.removeEventListener('click', handler);
742
- +el.addEventListener('click', handler);
743
- ```
744
-
745
- #### Dispatching and Ready
746
-
747
- ```diff
748
- -$(el).trigger('custom');
749
- -$(document).ready(fn);
750
- +el.dispatchEvent(new CustomEvent('custom'));
751
- +document.addEventListener('DOMContentLoaded', fn);
752
- ```
753
-
754
- ### Traversal
755
-
756
- #### Traversal helpers
757
-
758
- ```diff
759
- -$(el).find('.sel');
760
- -$(el).parent();
761
- -$(el).closest('.sel');
762
- -$(el).next();
763
- -$(el).prev();
764
- -$(el).children();
765
- -$(el).siblings();
766
- +el.querySelectorAll('.sel');
767
- +el.parentElement;
768
- +el.closest('.sel');
769
- +el.nextElementSibling;
770
- +el.previousElementSibling;
771
- +Array.from(el.children);
772
- +Array.from(el.parentElement.children).filter(c => c !== el);
773
- ```
774
-
775
- ### Style and Sizing
776
-
777
- #### CSS and Display
778
-
779
- ```diff
780
- -$(el).css('color');
781
- -$(el).css('color', 'red');
782
- -$(el).show();
783
- -$(el).hide();
784
- +getComputedStyle(el).color;
785
- +el.style.color = 'red';
786
- +el.style.display = '';
787
- +el.style.display = 'none';
788
- ```
789
-
790
- #### Dimensions
791
-
792
- ```diff
793
- -$(el).width();
794
- -$(el).height();
795
- +el.clientWidth;
796
- +el.clientHeight;
797
- ```
798
-
799
- ### Static Utilities
800
-
801
- #### Collection and String utilities
716
+ #### `Object.prototype.hasOwnProperty.call()` → [Object.hasOwn()][mdn-object-has-own]
802
717
 
803
718
  ```diff
804
- -$.each(array, fn);
805
- -$.map(array, fn);
806
- -$.inArray(val, array) !== -1;
807
- -$.trim(str);
808
- +array.forEach(fn);
809
- +array.map(fn);
810
- +array.includes(val);
811
- +str.trim();
719
+ -Object.prototype.hasOwnProperty.call(obj, prop);
720
+ -({}).hasOwnProperty.call(obj, prop);
721
+ +Object.hasOwn(obj, prop);
722
+ +Object.hasOwn(obj, prop);
812
723
  ```
813
724
 
814
725
  ## Versioning
@@ -833,10 +744,12 @@ Furthermore, esupgrade supports JavaScript, TypeScript, and more, while lebab is
833
744
  [baseline]: https://web.dev/baseline/
834
745
  [calver]: https://calver.org/
835
746
  [django-upgrade]: https://github.com/adamchainz/django-upgrade
836
- [jquery]: https://jquery.com/
747
+ [husky]: https://typicode.github.io/husky/
837
748
  [mdn-arrow-functions]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
838
749
  [mdn-async-await]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
750
+ [mdn-at]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at
839
751
  [mdn-classes]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
752
+ [mdn-compound-assignment]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Addition_assignment
840
753
  [mdn-console]: https://developer.mozilla.org/en-US/docs/Web/API/console
841
754
  [mdn-const]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
842
755
  [mdn-default-parameters]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters
@@ -849,10 +762,14 @@ Furthermore, esupgrade supports JavaScript, TypeScript, and more, while lebab is
849
762
  [mdn-globalthis]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis
850
763
  [mdn-includes]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
851
764
  [mdn-let]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
765
+ [mdn-logical-assignment]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR_assignment
852
766
  [mdn-nullish-coalescing]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing
767
+ [mdn-numeric-separators]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#numeric_separators
853
768
  [mdn-object-entries]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
769
+ [mdn-object-has-own]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn
854
770
  [mdn-object-values]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values
855
771
  [mdn-promise-try]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/try
772
+ [mdn-replaceall]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll
856
773
  [mdn-rest-parameters]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters
857
774
  [mdn-slice]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice
858
775
  [mdn-spread]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
package/SKILL.md ADDED
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: esupgrade
3
+ description: Auto-update JavaScript and TypeScript syntax to new ECMAScript features based on browser support.
4
+ ---
5
+
6
+ Use the CLI with `npx` on files or directories:
7
+
8
+ ```console
9
+ npx esupgrade [--baseline <newly-available|widely-available>] [--check] [--write] <files-or-directories>
10
+ ```
11
+
12
+ - Use `--check` to preview changes and fail when updates are needed.
13
+ - Use `--write` to apply updates in place.
14
+ - Use `--baseline` to choose `newly-available` or `widely-available` (default).
package/bin/esupgrade.js CHANGED
@@ -30,12 +30,11 @@ class WorkerRunner {
30
30
  * Run a worker thread to process a file.
31
31
  * @param {string} filePath - Path to the file to process.
32
32
  * @param {string} baseline - Baseline level for transformations.
33
- * @param {boolean} jQuery - Whether to include jQuery transformers.
34
33
  * @returns {Promise<Object>} Worker message result.
35
34
  */
36
- async run(filePath, baseline, jQuery) {
35
+ async run(filePath, baseline) {
37
36
  const worker = new Worker(this.workerPath, {
38
- workerData: { filePath, baseline, jQuery },
37
+ workerData: { filePath, baseline },
39
38
  })
40
39
 
41
40
  const [message] = await once(worker, "message")
@@ -59,7 +58,6 @@ class FileProcessor {
59
58
  * @param {boolean} options.check - Whether to only check for changes.
60
59
  * @param {boolean} options.write - Whether to write changes to file.
61
60
  * @param {boolean} options.verbose - The verbosity level for logging.
62
- * @param {boolean} options.jQuery - Whether to include jQuery transformers.
63
61
  * @returns {Promise<{modified: boolean, error: boolean}>} Result of processing.
64
62
  */
65
63
  async processFile(filePath, options) {
@@ -76,11 +74,7 @@ class FileProcessor {
76
74
  }
77
75
 
78
76
  try {
79
- const workerResult = await this.workerRunner.run(
80
- filePath,
81
- options.baseline,
82
- options.jQuery,
83
- )
77
+ const workerResult = await this.workerRunner.run(filePath, options.baseline)
84
78
 
85
79
  if (!workerResult.success) {
86
80
  if (options.verbose) console.error(workerResult.error)
@@ -203,7 +197,6 @@ class CLIRunner {
203
197
  async run(patterns, options) {
204
198
  console.time("Processing")
205
199
  // Hand CLI-provided names directly to the worker pool; file validation occurs in processFile.
206
- // Pass jQuery to worker pool
207
200
  const results = await this.workerPool.processFiles(patterns, options)
208
201
  console.timeEnd("Processing")
209
202
 
@@ -277,7 +270,6 @@ program
277
270
  false,
278
271
  )
279
272
  .option("--write", "Write changes to files", false)
280
- .option("--jquery, --jQuery", "Enable jQuery specific transformers", false)
281
273
  .action(async (files, options) => {
282
274
  await cliRunner.run(files, options)
283
275
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "esupgrade",
3
- "version": "2025.19.0",
3
+ "version": "2025.20.1",
4
4
  "description": "Auto-upgrade your JavaScript syntax",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import jscodeshift from "jscodeshift"
2
2
  import * as newlyAvailable from "./newlyAvailable.js"
3
3
  import * as widelyAvailable from "./widelyAvailable.js"
4
- import * as jQueryTransformers from "./jQuery.js"
5
4
 
6
5
  /**
7
6
  * Transformer function type.
@@ -49,10 +48,9 @@ function applyTransformersRecursively(code, j, transformers, globalModified = fa
49
48
  *
50
49
  * @param {string} code - The source code to transform.
51
50
  * @param {string} baseline - Baseline level ('widely-available' or 'newly-available').
52
- * @param {boolean} jQuery - Whether to include jQuery transformers.
53
51
  * @returns {TransformResult} Object with transformed code and modification status.
54
52
  */
55
- export function transform(code, baseline = "widely-available", jQuery) {
53
+ export function transform(code, baseline = "widely-available") {
56
54
  const j = jscodeshift.withParser("tsx")
57
55
 
58
56
  let transformers =
@@ -60,9 +58,5 @@ export function transform(code, baseline = "widely-available", jQuery) {
60
58
  ? { ...widelyAvailable, ...newlyAvailable }
61
59
  : widelyAvailable
62
60
 
63
- if (jQuery) {
64
- transformers = { ...transformers, ...jQueryTransformers }
65
- }
66
-
67
61
  return applyTransformersRecursively(code, j, Object.values(transformers))
68
62
  }
@@ -151,3 +151,4 @@ export function promiseTry(root) {
151
151
 
152
152
  return modified
153
153
  }
154
+ promiseTry.baselineDate = new Date(Date.UTC(2025, 0, 7))
@@ -75,3 +75,4 @@ export function anonymousFunctionToArrow(root) {
75
75
 
76
76
  return modified
77
77
  }
78
+ anonymousFunctionToArrow.baselineDate = new Date(Date.UTC(2016, 8, 20))
@@ -26,8 +26,7 @@ export function argumentsToRestParameters(root) {
26
26
 
27
27
  functionNodes.forEach(({ node: func }) => {
28
28
  if (
29
- (func.params.length > 0 &&
30
- j.RestElement.check(func.params[func.params.length - 1])) ||
29
+ (func.params.length > 0 && j.RestElement.check(func.params.at(-1))) ||
31
30
  !j.BlockStatement.check(func.body)
32
31
  ) {
33
32
  return
@@ -86,3 +85,4 @@ export function argumentsToRestParameters(root) {
86
85
 
87
86
  return modified
88
87
  }
88
+ argumentsToRestParameters.baselineDate = new Date(Date.UTC(2016, 8, 20))
@@ -65,3 +65,4 @@ export function arrayConcatToSpread(root) {
65
65
 
66
66
  return modified
67
67
  }
68
+ arrayConcatToSpread.baselineDate = new Date(Date.UTC(2015, 9, 14))
@@ -76,3 +76,4 @@ export function arrayFilterToFind(root) {
76
76
 
77
77
  return modified
78
78
  }
79
+ arrayFilterToFind.baselineDate = new Date(Date.UTC(2016, 8, 20))
@@ -72,3 +72,4 @@ export function arrayFromForEachToForOf(root) {
72
72
 
73
73
  return modified
74
74
  }
75
+ arrayFromForEachToForOf.baselineDate = new Date(Date.UTC(2016, 8, 20))
@@ -56,3 +56,4 @@ export function arrayFromToSpread(root) {
56
56
 
57
57
  return modified
58
58
  }
59
+ arrayFromToSpread.baselineDate = new Date(Date.UTC(2016, 8, 20))
@@ -54,3 +54,4 @@ export function arraySliceToSpread(root) {
54
54
 
55
55
  return modified
56
56
  }
57
+ arraySliceToSpread.baselineDate = new Date(Date.UTC(2015, 9, 14))
@@ -0,0 +1,53 @@
1
+ import { default as j } from "jscodeshift"
2
+ import { NodeTest } from "../types.js"
3
+
4
+ const BINARY_TO_COMPOUND = new Map([
5
+ ["+", "+="],
6
+ ["-", "-="],
7
+ ["*", "*="],
8
+ ["/", "/="],
9
+ ["%", "%="],
10
+ ["**", "**="],
11
+ ])
12
+
13
+ /**
14
+ * Transform verbose assignment patterns to compound assignment operators.
15
+ *
16
+ * Transforms:
17
+ * - `x = x + y` → `x += y`
18
+ * - `x = x - y` → `x -= y`
19
+ * - `x = x * y` → `x *= y`
20
+ * - `x = x / y` → `x /= y`
21
+ * - `x = x % y` → `x %= y`
22
+ * - `x = x ** y` → `x **= y`
23
+ *
24
+ * @param {import("jscodeshift").Collection} root - The root AST collection
25
+ * @returns {boolean} True if code was modified
26
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Addition_assignment
27
+ */
28
+ export function compoundAssignment(root) {
29
+ let modified = false
30
+
31
+ root
32
+ .find(j.AssignmentExpression, { operator: "=" })
33
+ .filter(({ node }) => {
34
+ if (!j.BinaryExpression.check(node.right)) return false
35
+ const op = node.right.operator
36
+ if (!BINARY_TO_COMPOUND.has(op)) return false
37
+ return new NodeTest(node.left).isEqual(node.right.left)
38
+ })
39
+ .forEach((path) => {
40
+ const { node } = path
41
+ const op = node.right.operator
42
+ const right = node.right.right
43
+
44
+ j(path).replaceWith(
45
+ j.assignmentExpression(BINARY_TO_COMPOUND.get(op), node.left, right),
46
+ )
47
+ modified = true
48
+ })
49
+
50
+ return modified
51
+ }
52
+
53
+ compoundAssignment.baselineDate = new Date(Date.UTC(1997, 5, 1))