codemod 1.7.6 → 1.7.7

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 (3) hide show
  1. package/README.md +11 -6
  2. package/codemod +112 -35
  3. package/package.json +9 -6
package/README.md CHANGED
@@ -30,32 +30,37 @@ Whether you're an individual developer tackling tech debt, an OSS maintainer shi
30
30
  ## Installation
31
31
 
32
32
  ```bash
33
- npm install -g codemod@latest
33
+ npm install -g codemod
34
34
  ```
35
35
 
36
36
  Or use via `npx` without installation:
37
37
 
38
38
  ```bash
39
- npx codemod@latest <command>
39
+ npx codemod
40
40
  ```
41
41
 
42
+ In an interactive terminal, bare `npx codemod` opens a launcher and refreshes to the latest published CLI before showing the prompt. In non-interactive contexts, it prints next steps and exits with status `1`.
43
+
42
44
  ## Quick Start
43
45
 
44
46
  ```bash
45
- # 1. Create a codemod package
47
+ # 1. Start with the launcher or scaffold directly
48
+ npx codemod
49
+
50
+ # 2. Create a codemod package
46
51
  npx codemod init my-codemod
47
52
  cd my-codemod
48
53
 
49
54
  # You can create codemod packages with the help of AI using Codemod MCP or Studio
50
55
 
51
- # 2. Run it locally
56
+ # 3. Run it locally
52
57
  npx codemod workflow run -w ./example-codemod -t /abs/path/to/repo
53
58
 
54
- # 3. Publish to registry
59
+ # 4. Publish to registry
55
60
  npx codemod login
56
61
  npx codemod publish
57
62
 
58
- # 4. Run from registry
63
+ # 5. Run from registry
59
64
  npx codemod @your-org/example-codemod
60
65
  ```
61
66
 
package/codemod CHANGED
@@ -5,6 +5,7 @@ const fs = require("fs");
5
5
  const path = require("path");
6
6
 
7
7
  const binaryName = process.platform === "win32" ? "codemod.exe" : "codemod";
8
+ const CODEMOD_BOOTSTRAPPED_ENV = "CODEMOD_BOOTSTRAPPED";
8
9
 
9
10
  function detectPackageName() {
10
11
  const { platform, arch } = process;
@@ -93,8 +94,6 @@ function resolveBinaryPath() {
93
94
  }
94
95
  }
95
96
 
96
- console.log("pkgName", pkgName);
97
-
98
97
  const releaseCandidate = path.join(
99
98
  __dirname,
100
99
  "..",
@@ -125,43 +124,121 @@ function resolveBinaryPath() {
125
124
  failToLocateBinary(pkgName, moduleError);
126
125
  }
127
126
 
128
- const { binaryPath } = resolveBinaryPath();
127
+ function npmExecutable(platform = process.platform) {
128
+ return platform === "win32" ? "npm.cmd" : "npm";
129
+ }
129
130
 
130
- const child = spawn(binaryPath, process.argv.slice(2), {
131
- stdio: "inherit",
132
- });
131
+ function latestBootstrapArgs() {
132
+ return [
133
+ "exec",
134
+ "--yes",
135
+ "--prefer-online",
136
+ "--package",
137
+ "codemod@latest",
138
+ "--",
139
+ "codemod",
140
+ ];
141
+ }
133
142
 
134
- child.on("error", (error) => {
135
- console.error("Failed to execute codemod binary.");
136
- console.error(error);
137
- process.exit(1);
138
- });
143
+ function shouldBootstrapLatestNoCommand(argv, env = process.env) {
144
+ return argv.length === 0 && !env[CODEMOD_BOOTSTRAPPED_ENV];
145
+ }
139
146
 
140
- const signals = ["SIGINT", "SIGTERM", "SIGHUP"];
147
+ function spawnLatestCli({ env = process.env, platform = process.platform, spawnImpl = spawn }) {
148
+ return spawnImpl(npmExecutable(platform), latestBootstrapArgs(), {
149
+ stdio: "inherit",
150
+ env: {
151
+ ...env,
152
+ [CODEMOD_BOOTSTRAPPED_ENV]: "1",
153
+ },
154
+ });
155
+ }
141
156
 
142
- const forwarders = signals.map((signal) => {
143
- const handler = () => {
144
- if (child.killed) {
145
- return;
146
- }
147
- try {
148
- child.kill(signal);
149
- } catch (_) {
150
- // Ignore
151
- }
152
- };
153
- process.on(signal, handler);
154
- return { signal, handler };
155
- });
156
-
157
- child.on("exit", (code, signal) => {
158
- forwarders.forEach(({ signal, handler }) => {
159
- process.removeListener(signal, handler);
157
+ function spawnResolvedBinary({
158
+ argv,
159
+ spawnImpl = spawn,
160
+ resolveBinaryPathImpl = resolveBinaryPath,
161
+ }) {
162
+ const { binaryPath } = resolveBinaryPathImpl();
163
+ return spawnImpl(binaryPath, argv, {
164
+ stdio: "inherit",
160
165
  });
166
+ }
161
167
 
162
- if (signal) {
163
- process.kill(process.pid, signal);
164
- } else {
165
- process.exit(code == null ? 1 : code);
168
+ function run({
169
+ argv = process.argv.slice(2),
170
+ env = process.env,
171
+ platform = process.platform,
172
+ spawnImpl = spawn,
173
+ resolveBinaryPathImpl = resolveBinaryPath,
174
+ } = {}) {
175
+ if (shouldBootstrapLatestNoCommand(argv, env)) {
176
+ return spawnLatestCli({ env, platform, spawnImpl });
166
177
  }
167
- });
178
+
179
+ return spawnResolvedBinary({ argv, spawnImpl, resolveBinaryPathImpl });
180
+ }
181
+
182
+ function attachChildLifecycle(
183
+ child,
184
+ {
185
+ signalSource = process,
186
+ processObject = process,
187
+ logger = console.error,
188
+ } = {},
189
+ ) {
190
+ child.on("error", (error) => {
191
+ logger("Failed to execute codemod binary.");
192
+ logger(error);
193
+ processObject.exit(1);
194
+ });
195
+
196
+ const signals = ["SIGINT", "SIGTERM", "SIGHUP"];
197
+
198
+ const forwarders = signals.map((signal) => {
199
+ const handler = () => {
200
+ if (child.killed) {
201
+ return;
202
+ }
203
+ try {
204
+ child.kill(signal);
205
+ } catch (_) {
206
+ // Ignore
207
+ }
208
+ };
209
+ signalSource.on(signal, handler);
210
+ return { signal, handler };
211
+ });
212
+
213
+ child.on("exit", (code, signal) => {
214
+ forwarders.forEach(({ signal, handler }) => {
215
+ signalSource.removeListener(signal, handler);
216
+ });
217
+
218
+ if (signal) {
219
+ processObject.kill(processObject.pid, signal);
220
+ } else {
221
+ processObject.exit(code == null ? 1 : code);
222
+ }
223
+ });
224
+ }
225
+
226
+ function main() {
227
+ const child = run();
228
+ attachChildLifecycle(child);
229
+ }
230
+
231
+ if (require.main === module) {
232
+ main();
233
+ }
234
+
235
+ module.exports = {
236
+ CODEMOD_BOOTSTRAPPED_ENV,
237
+ latestBootstrapArgs,
238
+ npmExecutable,
239
+ shouldBootstrapLatestNoCommand,
240
+ spawnLatestCli,
241
+ spawnResolvedBinary,
242
+ run,
243
+ attachChildLifecycle,
244
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codemod",
3
- "version": "1.7.6",
3
+ "version": "1.7.7",
4
4
  "description": "Codemod platform for semantic code transformations",
5
5
  "keywords": [
6
6
  "ast",
@@ -19,15 +19,18 @@
19
19
  "README.md",
20
20
  "codemod"
21
21
  ],
22
+ "scripts": {
23
+ "test": "node --test tests/*.test.js"
24
+ },
22
25
  "dependencies": {
23
26
  "detect-libc": "^2.0.3"
24
27
  },
25
28
  "optionalDependencies": {
26
- "@codemod.com/cli-darwin-arm64": "1.7.6",
27
- "@codemod.com/cli-darwin-x64": "1.7.6",
28
- "@codemod.com/cli-linux-arm64-gnu": "1.7.6",
29
- "@codemod.com/cli-linux-x64-gnu": "1.7.6",
30
- "@codemod.com/cli-win32-x64-msvc": "1.7.6"
29
+ "@codemod.com/cli-darwin-arm64": "1.7.7",
30
+ "@codemod.com/cli-darwin-x64": "1.7.7",
31
+ "@codemod.com/cli-linux-arm64-gnu": "1.7.7",
32
+ "@codemod.com/cli-linux-x64-gnu": "1.7.7",
33
+ "@codemod.com/cli-win32-x64-msvc": "1.7.7"
31
34
  },
32
35
  "engines": {
33
36
  "node": ">= 16.0.0"