esupgrade 2025.0.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/.editorconfig +27 -0
- package/.github/dependabot.yml +10 -0
- package/.github/workflows/ci.yml +23 -0
- package/.github/workflows/release.yml +23 -0
- package/.idea/copilot.data.migration.ask2agent.xml +6 -0
- package/.idea/esupgrade.iml +8 -0
- package/.idea/inspectionProfiles/Project_Default.xml +28 -0
- package/.idea/inspectionProfiles/profiles_settings.xml +6 -0
- package/.idea/misc.xml +7 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +7 -0
- package/.pre-commit-config.yaml +53 -0
- package/.pre-commit-hooks.yaml +7 -0
- package/LICENSE +24 -0
- package/README.md +152 -0
- package/bin/esupgrade.js +199 -0
- package/images/logo-dark.svg +11 -0
- package/images/logo-light.svg +11 -0
- package/package.json +38 -0
- package/src/index.js +712 -0
- package/tests/transform.test.js +322 -0
package/.editorconfig
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# http://editorconfig.org
|
|
2
|
+
|
|
3
|
+
root = true
|
|
4
|
+
|
|
5
|
+
[*]
|
|
6
|
+
indent_style = space
|
|
7
|
+
indent_size = 4
|
|
8
|
+
trim_trailing_whitespace = true
|
|
9
|
+
insert_final_newline = true
|
|
10
|
+
charset = utf-8
|
|
11
|
+
end_of_line = lf
|
|
12
|
+
max_line_length = 88
|
|
13
|
+
|
|
14
|
+
[*.{json,yml,yaml,mjs,js,jsx,vue,toml}]
|
|
15
|
+
indent_size = 2
|
|
16
|
+
ij_javascript_force_semicolon_style = false
|
|
17
|
+
|
|
18
|
+
[*.{html,htm,svg,xml}]
|
|
19
|
+
indent_size = 2
|
|
20
|
+
max_line_length = 120
|
|
21
|
+
|
|
22
|
+
[LICENSE]
|
|
23
|
+
insert_final_newline = false
|
|
24
|
+
|
|
25
|
+
[*.{md,markdown}]
|
|
26
|
+
indent_size = 2
|
|
27
|
+
max_line_length = 80
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches:
|
|
5
|
+
- main
|
|
6
|
+
pull_request:
|
|
7
|
+
jobs:
|
|
8
|
+
node-test:
|
|
9
|
+
name: Test
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v6
|
|
13
|
+
- name: Set up Node.js
|
|
14
|
+
uses: actions/setup-node@v6
|
|
15
|
+
with:
|
|
16
|
+
node-version-file: package.json
|
|
17
|
+
- run: npm ci
|
|
18
|
+
- run: node --test --experimental-test-coverage --test-reporter=spec --test-reporter=lcov --test-reporter-destination=stdout --test-reporter-destination=lcov.txt
|
|
19
|
+
- uses: codecov/codecov-action@v5
|
|
20
|
+
with:
|
|
21
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
22
|
+
flags: javascript
|
|
23
|
+
files: lcov.txt
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
on:
|
|
3
|
+
release:
|
|
4
|
+
types: [published]
|
|
5
|
+
permissions:
|
|
6
|
+
id-token: write
|
|
7
|
+
contents: read
|
|
8
|
+
jobs:
|
|
9
|
+
npmjs:
|
|
10
|
+
name: npmjs.org
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v6
|
|
14
|
+
- uses: actions/setup-node@v6
|
|
15
|
+
with:
|
|
16
|
+
node-version-file: package.json
|
|
17
|
+
registry-url: 'https://registry.npmjs.org'
|
|
18
|
+
- run: npm install -g npm@latest
|
|
19
|
+
- run: npm ci
|
|
20
|
+
- run: npm config set git-tag-version=false
|
|
21
|
+
- run: npm version ${{ github.event.release.tag_name }}
|
|
22
|
+
- run: npm run build --if-present
|
|
23
|
+
- run: npm publish
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module type="PYTHON_MODULE" version="4">
|
|
3
|
+
<component name="NewModuleRootManager">
|
|
4
|
+
<content url="file://$MODULE_DIR$" />
|
|
5
|
+
<orderEntry type="jdk" jdkName="Python 3.9" jdkType="Python SDK" />
|
|
6
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
|
7
|
+
</component>
|
|
8
|
+
</module>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<component name="InspectionProjectProfileManager">
|
|
2
|
+
<profile version="1.0">
|
|
3
|
+
<option name="myName" value="Project Default" />
|
|
4
|
+
<inspection_tool class="DjangoUnresolvedUrlInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
|
5
|
+
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
|
6
|
+
<inspection_tool class="JsonSchemaCompliance" enabled="false" level="WARNING" enabled_by_default="false" />
|
|
7
|
+
<inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
|
8
|
+
<option name="ourVersions">
|
|
9
|
+
<value>
|
|
10
|
+
<list size="2">
|
|
11
|
+
<item index="0" class="java.lang.String" itemvalue="3.13" />
|
|
12
|
+
<item index="1" class="java.lang.String" itemvalue="3.14" />
|
|
13
|
+
</list>
|
|
14
|
+
</value>
|
|
15
|
+
</option>
|
|
16
|
+
</inspection_tool>
|
|
17
|
+
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
|
18
|
+
<option name="ignoredIdentifiers">
|
|
19
|
+
<list>
|
|
20
|
+
<option value="ssdp.lexers.content_callback" />
|
|
21
|
+
</list>
|
|
22
|
+
</option>
|
|
23
|
+
</inspection_tool>
|
|
24
|
+
<inspection_tool class="StandardJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
|
25
|
+
<inspection_tool class="Stylelint" enabled="true" level="ERROR" enabled_by_default="true" />
|
|
26
|
+
<inspection_tool class="TsLint" enabled="true" level="WARNING" enabled_by_default="true" />
|
|
27
|
+
</profile>
|
|
28
|
+
</component>
|
package/.idea/misc.xml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="Black">
|
|
4
|
+
<option name="sdkName" value="Python 3.9" />
|
|
5
|
+
</component>
|
|
6
|
+
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9" project-jdk-type="Python SDK" />
|
|
7
|
+
</project>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ProjectModuleManager">
|
|
4
|
+
<modules>
|
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/esupgrade.iml" filepath="$PROJECT_DIR$/.idea/esupgrade.iml" />
|
|
6
|
+
</modules>
|
|
7
|
+
</component>
|
|
8
|
+
</project>
|
package/.idea/vcs.xml
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v6.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: check-merge-conflict
|
|
7
|
+
- id: check-json
|
|
8
|
+
- id: check-yaml
|
|
9
|
+
- id: debug-statements
|
|
10
|
+
- id: destroyed-symlinks
|
|
11
|
+
- id: check-executables-have-shebangs
|
|
12
|
+
- id: end-of-file-fixer
|
|
13
|
+
- id: no-commit-to-branch
|
|
14
|
+
args: [--branch, main]
|
|
15
|
+
- id: detect-private-key
|
|
16
|
+
- repo: https://github.com/hukkin/mdformat
|
|
17
|
+
rev: 1.0.0
|
|
18
|
+
hooks:
|
|
19
|
+
- id: mdformat
|
|
20
|
+
additional_dependencies:
|
|
21
|
+
- mdformat-footnote
|
|
22
|
+
- mdformat-gfm
|
|
23
|
+
- mdformat-gfm-alerts
|
|
24
|
+
- repo: https://github.com/google/yamlfmt
|
|
25
|
+
rev: v0.20.0
|
|
26
|
+
hooks:
|
|
27
|
+
- id: yamlfmt
|
|
28
|
+
- repo: https://github.com/btford/write-good
|
|
29
|
+
rev: v1.0.8
|
|
30
|
+
hooks:
|
|
31
|
+
- id: write-good
|
|
32
|
+
args: [--no-passive]
|
|
33
|
+
- repo: local
|
|
34
|
+
hooks:
|
|
35
|
+
- id: prettier
|
|
36
|
+
name: prettier
|
|
37
|
+
entry: prettier --check --write --ignore-unknown
|
|
38
|
+
args:
|
|
39
|
+
- --config=package.json
|
|
40
|
+
language: node
|
|
41
|
+
additional_dependencies: ["prettier"]
|
|
42
|
+
types_or: [javascript]
|
|
43
|
+
# - id: esupgrade
|
|
44
|
+
# name: esupgrade
|
|
45
|
+
# entry: npx . --check
|
|
46
|
+
# args: [--write]
|
|
47
|
+
# language: system
|
|
48
|
+
# types_or: [javascript]
|
|
49
|
+
ci:
|
|
50
|
+
autoupdate_schedule: weekly
|
|
51
|
+
skip:
|
|
52
|
+
- no-commit-to-branch
|
|
53
|
+
- write-good
|
package/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
BSD 2-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, Johannes Maron
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
16
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
17
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
19
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
20
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
21
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
22
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
23
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
24
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="./images/logo-dark.svg">
|
|
4
|
+
<source media="(prefers-color-scheme: light)" srcset="./images/logo-light.svg">
|
|
5
|
+
<img alt="esupgrade: Auto-upgrade your JavaScript syntax" src="./images/logo-light.svg">
|
|
6
|
+
</picture>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
# esupgrade
|
|
10
|
+
|
|
11
|
+
Keeping your JavaScript and TypeScript code up to date with full browser compatibility.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### CLI
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx esupgrade --help
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### pre-commit
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
uvx pre-commit install
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```yaml
|
|
28
|
+
# .pre-commit-config.yaml
|
|
29
|
+
repos:
|
|
30
|
+
- repo: https://github.com/codingjoe/esupgrade
|
|
31
|
+
rev: v0.1.0 # Use the latest version
|
|
32
|
+
hooks:
|
|
33
|
+
- id: esupgrade
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pre-commit run esupgrade --all-files
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Browser Support & Baseline
|
|
41
|
+
|
|
42
|
+
<picture>
|
|
43
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://web-platform-dx.github.io/web-features/assets/img/baseline-widely-word-dark.svg">
|
|
44
|
+
<source media="(prefers-color-scheme: light)" srcset="https://web-platform-dx.github.io/web-features/assets/img/baseline-widely-word.svg">
|
|
45
|
+
<img alt="Baseline: widely available" src="https://web-platform-dx.github.io/web-features/assets/img/baseline-widely-word.svg" height="32" align="right">
|
|
46
|
+
</picture>
|
|
47
|
+
|
|
48
|
+
All transformations are based on [Web Platform Baseline](https://web.dev/baseline) features. Baseline tracks which web platform features are safe to use across browsers.
|
|
49
|
+
|
|
50
|
+
By default, `esupgrade` uses **widely available** features, meaning they work in all major browsers (Chrome, Edge, Safari, Firefox) for at least 30 months. This ensures full compatibility while keeping your code modern.
|
|
51
|
+
|
|
52
|
+
You can opt into **newly available** features (available in all browsers for 0-30 months) with:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx esupgrade --baseline newly-available src/
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
For more information about Baseline browser support, visit [web.dev/baseline](https://web.dev/baseline).
|
|
59
|
+
|
|
60
|
+
## Transformations
|
|
61
|
+
|
|
62
|
+
All transformations are safe and behavior-preserving. Here's what `esupgrade` does:
|
|
63
|
+
|
|
64
|
+
### 1. `var` → `let`/`const`
|
|
65
|
+
|
|
66
|
+
```diff
|
|
67
|
+
-var x = 1;
|
|
68
|
+
-var y = 2;
|
|
69
|
+
-y = 3;
|
|
70
|
+
+const x = 1;
|
|
71
|
+
+let y = 2;
|
|
72
|
+
+y = 3;
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 2. String concatenation → Template literals
|
|
76
|
+
|
|
77
|
+
```diff
|
|
78
|
+
-const greeting = 'Hello ' + name + '!';
|
|
79
|
+
-const message = 'You have ' + count + ' items';
|
|
80
|
+
+const greeting = `Hello ${name}!`;
|
|
81
|
+
+const message = `You have ${count} items`;
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 3. `Array.from().forEach()` → `for...of` loops
|
|
85
|
+
|
|
86
|
+
```diff
|
|
87
|
+
-Array.from(items).forEach(item => {
|
|
88
|
+
- console.log(item);
|
|
89
|
+
-});
|
|
90
|
+
+for (const item of items) {
|
|
91
|
+
+ console.log(item);
|
|
92
|
+
+}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 4. `Object.assign({}, ...)` → Object spread
|
|
96
|
+
|
|
97
|
+
```diff
|
|
98
|
+
-const obj = Object.assign({}, obj1, obj2);
|
|
99
|
+
-const copy = Object.assign({}, original);
|
|
100
|
+
+const obj = { ...obj1, ...obj2 };
|
|
101
|
+
+const copy = { ...original };
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 5. `.concat()` → Array spread
|
|
105
|
+
|
|
106
|
+
```diff
|
|
107
|
+
-const combined = arr1.concat(arr2, arr3);
|
|
108
|
+
-const withItem = array.concat([item]);
|
|
109
|
+
+const combined = [...arr1, ...arr2, ...arr3];
|
|
110
|
+
+const withItem = [...array, item];
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 6. Function expressions → Arrow functions
|
|
114
|
+
|
|
115
|
+
```diff
|
|
116
|
+
-const fn = function(x) { return x * 2; };
|
|
117
|
+
-items.map(function(item) { return item.name; });
|
|
118
|
+
+const fn = x => { return x * 2; };
|
|
119
|
+
+items.map(item => { return item.name; });
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Note:** Functions using `this`, `arguments`, or `super` are not converted to preserve semantics.
|
|
123
|
+
|
|
124
|
+
### Complete Example
|
|
125
|
+
|
|
126
|
+
```diff
|
|
127
|
+
-var userName = 'John';
|
|
128
|
+
-var userAge = 30;
|
|
129
|
+
-var greeting = 'Hello, ' + userName + '!';
|
|
130
|
+
+const userName = 'John';
|
|
131
|
+
+const userAge = 30;
|
|
132
|
+
+const greeting = `Hello, ${userName}!`;
|
|
133
|
+
|
|
134
|
+
-Array.from(users).forEach(function(user) {
|
|
135
|
+
- console.log('User: ' + user.name);
|
|
136
|
+
-});
|
|
137
|
+
+for (const user of users) {
|
|
138
|
+
+ console.log(`User: ${user.name}`);
|
|
139
|
+
+}
|
|
140
|
+
|
|
141
|
+
-var settings = Object.assign({}, defaultSettings, userSettings);
|
|
142
|
+
+const settings = { ...defaultSettings, ...userSettings };
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Supported File Types
|
|
146
|
+
|
|
147
|
+
- `.js` - JavaScript
|
|
148
|
+
- `.jsx` - React/JSX
|
|
149
|
+
- `.ts` - TypeScript
|
|
150
|
+
- `.tsx` - TypeScript with JSX
|
|
151
|
+
- `.mjs` - ES Modules
|
|
152
|
+
- `.cjs` - CommonJS
|
package/bin/esupgrade.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "fs"
|
|
4
|
+
import path from "path"
|
|
5
|
+
import { Command } from "commander"
|
|
6
|
+
import { transform } from "../src/index.js"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* CLI tool for esupgrade
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const program = new Command()
|
|
13
|
+
|
|
14
|
+
program
|
|
15
|
+
.name("esupgrade")
|
|
16
|
+
.description("Auto-upgrade your JavaScript syntax")
|
|
17
|
+
.version("0.1.0")
|
|
18
|
+
.argument("[files...]", "Files or directories to process")
|
|
19
|
+
.option(
|
|
20
|
+
"--baseline <level>",
|
|
21
|
+
"Set baseline level: widely-available (default) or newly-available",
|
|
22
|
+
"widely-available",
|
|
23
|
+
)
|
|
24
|
+
.option("--check", "Report which files need upgrading and exit with code 1 if any do")
|
|
25
|
+
.option(
|
|
26
|
+
"--write",
|
|
27
|
+
"Write changes to files (default: true unless only --check is specified)",
|
|
28
|
+
)
|
|
29
|
+
.action((files, options) => {
|
|
30
|
+
if (files.length === 0) {
|
|
31
|
+
console.error("Error: No files specified")
|
|
32
|
+
program.help()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Validate baseline option
|
|
36
|
+
if (!["widely-available", "newly-available"].includes(options.baseline)) {
|
|
37
|
+
console.error(`Error: Invalid baseline level '${options.baseline}'`)
|
|
38
|
+
console.error(`Must be 'widely-available' or 'newly-available'`)
|
|
39
|
+
process.exit(1)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Handle check/write options - they are not mutually exclusive
|
|
43
|
+
// Default: write is true unless ONLY --check is specified (no --write)
|
|
44
|
+
const shouldWrite = options.write !== undefined ? options.write : !options.check
|
|
45
|
+
const shouldCheck = options.check || false
|
|
46
|
+
|
|
47
|
+
const processingOptions = {
|
|
48
|
+
baseline: options.baseline,
|
|
49
|
+
write: shouldWrite,
|
|
50
|
+
check: shouldCheck,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
processFiles(files, processingOptions)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
function findFiles(patterns) {
|
|
57
|
+
const files = []
|
|
58
|
+
|
|
59
|
+
for (const pattern of patterns) {
|
|
60
|
+
try {
|
|
61
|
+
const stats = fs.statSync(pattern)
|
|
62
|
+
|
|
63
|
+
if (stats.isFile()) {
|
|
64
|
+
files.push(pattern)
|
|
65
|
+
} else if (stats.isDirectory()) {
|
|
66
|
+
// Recursively find .js, .jsx, .ts, .tsx files
|
|
67
|
+
const dirFiles = walkDirectory(pattern)
|
|
68
|
+
files.push(...dirFiles)
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error(`Error: Cannot access '${pattern}': ${error.message}`)
|
|
72
|
+
process.exit(1)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return files
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function walkDirectory(dir) {
|
|
80
|
+
const files = []
|
|
81
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true })
|
|
82
|
+
|
|
83
|
+
for (const entry of entries) {
|
|
84
|
+
const fullPath = path.join(dir, entry.name)
|
|
85
|
+
|
|
86
|
+
if (entry.isDirectory()) {
|
|
87
|
+
if (entry.name === "node_modules" || entry.name === ".git") {
|
|
88
|
+
continue
|
|
89
|
+
}
|
|
90
|
+
files.push(...walkDirectory(fullPath))
|
|
91
|
+
} else if (entry.isFile()) {
|
|
92
|
+
const ext = path.extname(entry.name)
|
|
93
|
+
if ([".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"].includes(ext)) {
|
|
94
|
+
files.push(fullPath)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return files
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function processFile(filePath, options) {
|
|
103
|
+
try {
|
|
104
|
+
const code = fs.readFileSync(filePath, "utf8")
|
|
105
|
+
const result = transform(code, { baseline: options.baseline })
|
|
106
|
+
|
|
107
|
+
if (result.modified) {
|
|
108
|
+
if (options.check) {
|
|
109
|
+
console.log(`✗ ${filePath}`)
|
|
110
|
+
if (result.changes && result.changes.length > 0) {
|
|
111
|
+
// Group changes by type
|
|
112
|
+
const changesByType = {}
|
|
113
|
+
|
|
114
|
+
for (const change of result.changes) {
|
|
115
|
+
if (!changesByType[change.type]) {
|
|
116
|
+
changesByType[change.type] = []
|
|
117
|
+
}
|
|
118
|
+
changesByType[change.type].push(change.line)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
for (const [type, lines] of Object.entries(changesByType)) {
|
|
122
|
+
const uniqueLines = [...new Set(lines)].sort((a, b) => a - b)
|
|
123
|
+
const displayName = type
|
|
124
|
+
.replace(/([A-Z])/g, " $1")
|
|
125
|
+
.trim()
|
|
126
|
+
.toLowerCase()
|
|
127
|
+
console.log(
|
|
128
|
+
` - ${displayName} (line${uniqueLines.length > 1 ? "s" : ""}: ${uniqueLines.join(", ")})`,
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (options.write) {
|
|
135
|
+
fs.writeFileSync(filePath, result.code, "utf8")
|
|
136
|
+
if (!options.check) {
|
|
137
|
+
console.log(`✓ Upgraded: ${filePath}`)
|
|
138
|
+
} else {
|
|
139
|
+
console.log(` ✓ Changes written to file`)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return true
|
|
144
|
+
} else {
|
|
145
|
+
if (!options.check) {
|
|
146
|
+
console.log(` No changes: ${filePath}`)
|
|
147
|
+
}
|
|
148
|
+
return false
|
|
149
|
+
}
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.error(`Error processing ${filePath}: ${error.message}`)
|
|
152
|
+
return false
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function processFiles(patterns, options) {
|
|
157
|
+
let files = []
|
|
158
|
+
try {
|
|
159
|
+
files = findFiles(patterns)
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.error(`Error finding files: ${error.message}`)
|
|
162
|
+
process.exit(1)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (files.length === 0) {
|
|
166
|
+
console.log("No JavaScript files found")
|
|
167
|
+
process.exit(0)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
console.log(`Processing ${files.length} file(s) with baseline: ${options.baseline}\n`)
|
|
171
|
+
|
|
172
|
+
let modifiedCount = 0
|
|
173
|
+
for (const file of files) {
|
|
174
|
+
if (processFile(file, options)) {
|
|
175
|
+
modifiedCount++
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Summary
|
|
180
|
+
console.log("")
|
|
181
|
+
if (modifiedCount > 0) {
|
|
182
|
+
if (options.write && options.check) {
|
|
183
|
+
console.log(`Summary: ${modifiedCount} file(s) upgraded`)
|
|
184
|
+
} else if (options.write) {
|
|
185
|
+
console.log(`Summary: ${modifiedCount} file(s) upgraded`)
|
|
186
|
+
} else {
|
|
187
|
+
console.log(`Summary: ${modifiedCount} file(s) need upgrading`)
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
console.log("Summary: All files are already modern")
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Exit with code 1 if --check specified and there were changes
|
|
194
|
+
if (options.check && modifiedCount > 0) {
|
|
195
|
+
process.exit(1)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
program.parse()
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
<circle cx="260" cy="320" r="200" fill="#f7df1e"/>
|
|
3
|
+
<path fill="none" stroke="#191919" stroke-width="48" d="m175 405 95-95 95 95m415 50"/>
|
|
4
|
+
<path fill="none" stroke="#191919" stroke-width="48" d="m175 315 95-95 95 95m415 50"/>
|
|
5
|
+
<text x="241" y="148" font-size="92" font-weight="bold" transform="matrix(1.6 0 0 1.6 120 120)" fill="#C9D1D9">
|
|
6
|
+
esupgrade
|
|
7
|
+
</text>
|
|
8
|
+
<text x="241" y="192" font-size="28" font-style="italic" transform="matrix(1.6 0 0 1.6 120 120)" fill="#C9D1D9">
|
|
9
|
+
Auto-upgrade your JavaScript syntax
|
|
10
|
+
</text>
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
<circle cx="260" cy="320" r="200" fill="#f7df1e"/>
|
|
3
|
+
<path fill="none" stroke="#191919" stroke-width="48" d="m175 405 95-95 95 95m415 50"/>
|
|
4
|
+
<path fill="none" stroke="#191919" stroke-width="48" d="m175 315 95-95 95 95m415 50"/>
|
|
5
|
+
<text x="241" y="148" font-size="92" font-weight="bold" transform="matrix(1.6 0 0 1.6 120 120)">
|
|
6
|
+
esupgrade
|
|
7
|
+
</text>
|
|
8
|
+
<text x="241" y="192" font-size="28" font-style="italic" transform="matrix(1.6 0 0 1.6 120 120)">
|
|
9
|
+
Auto-upgrade your JavaScript syntax
|
|
10
|
+
</text>
|
|
11
|
+
</svg>
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "esupgrade",
|
|
3
|
+
"version": "2025.0.0",
|
|
4
|
+
"description": "Auto-upgrade your JavaScript syntax",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"esupgrade": "./bin/esupgrade.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node --test --experimental-test-coverage --test-coverage-exclude=tests/* tests/*.test.js"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"javascript",
|
|
15
|
+
"ecmascript",
|
|
16
|
+
"modernize",
|
|
17
|
+
"upgrade",
|
|
18
|
+
"codemod",
|
|
19
|
+
"ast",
|
|
20
|
+
"transform"
|
|
21
|
+
],
|
|
22
|
+
"author": "Johannes Maron",
|
|
23
|
+
"license": "BSD-2-Clause",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"commander": "^14.0.2",
|
|
26
|
+
"jscodeshift": "^17.3.0"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=24.0.0"
|
|
30
|
+
},
|
|
31
|
+
"prettier": {
|
|
32
|
+
"semi": false
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/codingjoe/esupgrade.git"
|
|
37
|
+
}
|
|
38
|
+
}
|