torch-it 2.6.1 → 3.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/README.md +218 -18
- package/dist/torch-it.js +59 -24
- package/llms.txt +270 -0
- package/package.json +29 -23
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 🔥 Torch It
|
|
2
2
|
|
|
3
3
|
[](https://github.com/piyook/torch-it/releases)
|
|
4
|
-
[](https://github.com/piyook/torch-it/actions/workflows/tests.yaml)
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
[](https://www.npmjs.com/package/torch-it)
|
|
7
7
|
|
|
@@ -19,7 +19,7 @@ cd your-project
|
|
|
19
19
|
torch-it # clean and rebuild
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
`torch-it` will show you a preview of what it's about to delete and ask for confirmation before doing anything destructive.
|
|
22
|
+
`torch-it` will show you a preview of what it's about to delete and ask for confirmation before doing anything destructive. It never deletes unprompted: where there is no terminal to ask on (CI, pipes), it stops unless you pass `--yes`.
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
@@ -30,20 +30,22 @@ torch-it # clean and rebuild
|
|
|
30
30
|
1. Removes build artifacts and cache directories (50+ targets — `node_modules`, `dist`, `.next`, `.cache`, `.vite`, etc.)
|
|
31
31
|
2. Removes log files and temporary files (`*.log`, `*.tgz`, `*.tar.gz`, etc.)
|
|
32
32
|
3. Removes any custom paths you define in `torchrc.json`
|
|
33
|
-
4. Cleans
|
|
33
|
+
4. Cleans the cache of the package manager your project uses (npm, yarn, or pnpm, picked from the lockfile). This cache is shared by every project on your machine, so later installs elsewhere will download again. Skip it with `--cacheClean=false`
|
|
34
34
|
5. Reinstalls all dependencies
|
|
35
35
|
|
|
36
36
|
### Optionally runs (Docker mode)
|
|
37
37
|
|
|
38
|
-
When `dockerMode: true` and a `docker-compose.
|
|
38
|
+
When `dockerMode: true` and a Compose file is present (`compose.yaml`, `compose.yml`, `docker-compose.yaml` or `docker-compose.yml`):
|
|
39
39
|
|
|
40
40
|
| Step | When | Command |
|
|
41
41
|
|------|------|---------|
|
|
42
|
-
| Teardown | Before cleanup | `docker compose down --rmi all
|
|
42
|
+
| Teardown | Before cleanup | `docker compose down --rmi all` |
|
|
43
43
|
| Rebuild | After dependency install | `docker compose build --pull --no-cache` |
|
|
44
44
|
| Start | After successful rebuild | `docker compose up -d` |
|
|
45
45
|
|
|
46
|
-
> **
|
|
46
|
+
> **Volumes are kept by default.** Set `dockerVolumes: true` to add `--volumes` to the teardown. Anything stored in a volume, such as a development database, is then lost.
|
|
47
|
+
|
|
48
|
+
> **Note:** All Docker operations use `docker compose` (plugin). Ensure Docker Compose plugin is installed and on your PATH. A project with only a `Dockerfile` and no Compose file is left alone.
|
|
47
49
|
|
|
48
50
|
---
|
|
49
51
|
|
|
@@ -93,19 +95,128 @@ Shows a preview of what will be deleted, then prompts: **Continue? Type Yes or N
|
|
|
93
95
|
torch-it --yes # or -y
|
|
94
96
|
```
|
|
95
97
|
|
|
98
|
+
`--yes` is required when there is no interactive terminal. Without it, `torch-it` exits with code `1` and changes nothing.
|
|
99
|
+
|
|
100
|
+
Some Git Bash windows on Windows do not count as a terminal. If you get this message there, run `winpty torch-it`, or use PowerShell or Windows Terminal.
|
|
101
|
+
|
|
96
102
|
### Dry run (preview only, no changes)
|
|
97
103
|
|
|
98
104
|
```bash
|
|
99
105
|
torch-it --test
|
|
100
106
|
```
|
|
101
107
|
|
|
108
|
+
Lists every path that would be removed and every command that would run. Nothing is deleted and nothing is asked.
|
|
109
|
+
|
|
110
|
+
### Do less
|
|
111
|
+
|
|
112
|
+
A full torch is not always what you need. These narrow it down:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
torch-it --only=node_modules --cacheClean=false # just delete and reinstall dependencies
|
|
116
|
+
torch-it --only=dist,.next --rebuild=false # just clear some build output
|
|
117
|
+
torch-it --cacheClean=false # full clean, but leave the shared cache alone
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`--only` replaces the built-in targets, the file patterns and your `customPaths` with the paths you list. Protected paths still apply.
|
|
121
|
+
|
|
122
|
+
### Files tracked in git are kept
|
|
123
|
+
|
|
124
|
+
A file that is committed is somebody's work, not build output. If a target such as `build`, `out` or `tmp` holds files that git tracks, `torch-it` keeps those files and removes only what is around them:
|
|
125
|
+
|
|
126
|
+
- a `dist` folder holding one tracked `.gitkeep` is emptied, and the `.gitkeep` stays
|
|
127
|
+
- a `build` folder that is entirely committed is left untouched
|
|
128
|
+
- a tracked `debug.log` in the project root survives the `*.log` sweep
|
|
129
|
+
|
|
130
|
+
The preview and the summary say how many files were kept and where. To remove them as well, pass `--allowTracked=true`. Outside a git repository nothing changes.
|
|
131
|
+
|
|
132
|
+
### Run in another directory
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
torch-it --cwd=apps/web --test
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`torch-it` behaves exactly as if you had changed into that directory first, including reading its `torchrc.json`.
|
|
139
|
+
|
|
102
140
|
### Show current configuration
|
|
103
141
|
|
|
104
142
|
```bash
|
|
105
143
|
torch-it --config
|
|
106
144
|
```
|
|
107
145
|
|
|
108
|
-
Displays all active settings, every cleanup target, custom paths, protected paths, and Docker settings. Useful for verifying your setup before running.
|
|
146
|
+
Displays all active settings, every cleanup target, custom paths, protected paths, and Docker settings. Useful for verifying your setup before running. Command line overrides are included, so `torch-it --config --protectedPaths=dist` shows what that run would do.
|
|
147
|
+
|
|
148
|
+
### Output
|
|
149
|
+
|
|
150
|
+
At a terminal, `torch-it` uses colour, emoji and boxes. When its output is piped, redirected or captured (CI, scripts, AI agents), it switches to plain text on its own: no colour, emoji, banner or boxes, and one prefix per kind of line.
|
|
151
|
+
|
|
152
|
+
| Line starts with | Meaning | Goes to |
|
|
153
|
+
|------------------|---------|---------|
|
|
154
|
+
| `> ` | A step is starting | stdout |
|
|
155
|
+
| `ok: ` | Something succeeded | stdout |
|
|
156
|
+
| `warning: ` | Something was skipped or needs a look | stderr |
|
|
157
|
+
| `error: ` | Something failed | stderr |
|
|
158
|
+
| anything else | Information, and the summary at the end | stdout |
|
|
159
|
+
|
|
160
|
+
- `--plain` forces plain text at a terminal.
|
|
161
|
+
- `--quiet` prints only warnings, errors, the paths a dry run would remove and the final summary, and hides the progress output of the install and Docker commands. The plan you are asked to confirm is still shown.
|
|
162
|
+
- Set the `NO_COLOR` environment variable to drop colour and keep everything else.
|
|
163
|
+
|
|
164
|
+
### JSON output
|
|
165
|
+
|
|
166
|
+
`--json` prints a single JSON document on stdout and nothing else there. It never prompts, so it needs `--yes` or `--test`.
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
torch-it --test --json # the plan
|
|
170
|
+
torch-it --yes --json # the result
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"version": "3.0.0",
|
|
176
|
+
"ok": true,
|
|
177
|
+
"exitCode": 0,
|
|
178
|
+
"dryRun": true,
|
|
179
|
+
"changed": false,
|
|
180
|
+
"cwd": "/home/me/my-app",
|
|
181
|
+
"packageManager": "npm",
|
|
182
|
+
"removed": ["node_modules", "dist/bundle.js", "debug.log"],
|
|
183
|
+
"failed": [],
|
|
184
|
+
"keptTracked": ["dist/.gitkeep"],
|
|
185
|
+
"steps": {
|
|
186
|
+
"dockerTeardown": "skipped",
|
|
187
|
+
"cleanup": "ok",
|
|
188
|
+
"cacheClean": "ok",
|
|
189
|
+
"install": "ok",
|
|
190
|
+
"dockerRebuild": "skipped",
|
|
191
|
+
"dockerStart": "skipped"
|
|
192
|
+
},
|
|
193
|
+
"warnings": ["Kept 1 file(s) tracked in git, under: dist. Pass --allowTracked=true to remove them too."],
|
|
194
|
+
"errors": []
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
| Field | Meaning |
|
|
199
|
+
|-------|---------|
|
|
200
|
+
| `ok`, `exitCode` | The outcome, matching the process exit code |
|
|
201
|
+
| `dryRun`, `changed` | Whether this was `--test`, and whether anything on disk may have changed |
|
|
202
|
+
| `removed` | Paths removed, or in a dry run the paths that would be |
|
|
203
|
+
| `failed` | Paths that could not be removed |
|
|
204
|
+
| `keptTracked` | Files kept because git tracks them |
|
|
205
|
+
| `steps` | `ok`, `failed` or `skipped` for each step, plus `unavailable` for Docker. In a dry run `ok` means the step would run |
|
|
206
|
+
| `warnings`, `errors` | The warning and error lines of the run, as text |
|
|
207
|
+
|
|
208
|
+
When nothing was changed because of a bad option, a missing project or no way to confirm, the document is shorter: `ok: false`, `exitCode: 1`, `changed: false` and the `errors`. Warnings and errors are still written to stderr as they happen, and the output of the install and Docker commands goes to stderr too. `--version --json` and `--config --json` print the version and the resolved configuration.
|
|
209
|
+
|
|
210
|
+
### Exit codes
|
|
211
|
+
|
|
212
|
+
| Code | Meaning |
|
|
213
|
+
|------|---------|
|
|
214
|
+
| `0` | Every step that was meant to run succeeded, or there was nothing to do |
|
|
215
|
+
| `1` | Nothing was changed: invalid options or `torchrc.json`, not a Node.js project, or no terminal to confirm on and no `--yes` |
|
|
216
|
+
| `2` | The run went ahead and a step failed: a path could not be removed, dependency install failed, or Docker mode is on and Docker is unavailable or a Docker step failed |
|
|
217
|
+
| `3` | You answered no at the prompt. Nothing was changed |
|
|
218
|
+
|
|
219
|
+
A dry run that can see the real run would fail also exits with `2`.
|
|
109
220
|
|
|
110
221
|
---
|
|
111
222
|
|
|
@@ -118,7 +229,10 @@ Create a `torchrc.json` file in your project root to customise behaviour. Everyt
|
|
|
118
229
|
"customPaths": ["apps/web/.next", "services/api/tmp", "coverage-final.json"],
|
|
119
230
|
"protectedPaths": ["important-data/", "config/production.json"],
|
|
120
231
|
"dockerMode": false,
|
|
232
|
+
"dockerVolumes": false,
|
|
121
233
|
"rebuild": true,
|
|
234
|
+
"cacheClean": true,
|
|
235
|
+
"allowTracked": false,
|
|
122
236
|
"logfile": false
|
|
123
237
|
}
|
|
124
238
|
```
|
|
@@ -126,32 +240,59 @@ Create a `torchrc.json` file in your project root to customise behaviour. Everyt
|
|
|
126
240
|
| Option | Type | Default | Description |
|
|
127
241
|
|--------|------|---------|-------------|
|
|
128
242
|
| `customPaths` | `string[]` | `[]` | Extra directories or files to delete during cleanup |
|
|
129
|
-
| `protectedPaths` | `string[]` | `[]` | Paths to skip — preserved even if they match built-in targets |
|
|
243
|
+
| `protectedPaths` | `string[]` | `[]` | Paths to skip — preserved even if they match built-in targets. A protected path inside a target (e.g. `dist/keep.json`) is kept while the rest of the target is removed |
|
|
130
244
|
| `dockerMode` | `boolean` | `false` | Enable Docker teardown, rebuild, and launch |
|
|
245
|
+
| `dockerVolumes` | `boolean` | `false` | In Docker mode, also remove the project's volumes. Off by default because volumes can hold data that exists nowhere else |
|
|
131
246
|
| `rebuild` | `boolean` | `true` | Set to `false` to skip dependency reinstall and Docker rebuild (cleanup still runs) |
|
|
247
|
+
| `cacheClean` | `boolean` | `true` | Set to `false` to leave the package manager's machine-wide cache alone |
|
|
248
|
+
| `only` | `string[]` | `[]` | When set, remove only these paths. The built-in targets, file patterns and `customPaths` are skipped |
|
|
249
|
+
| `allowTracked` | `boolean` | `false` | Set to `true` to also remove files that git tracks. By default they are kept |
|
|
132
250
|
| `logfile` | `boolean` | `false` | Write runtime output to `torch-it.log` in the project root |
|
|
133
251
|
|
|
252
|
+
`customDirs` and `customFiles` are also accepted and behave exactly like `customPaths`.
|
|
253
|
+
|
|
254
|
+
Paths are relative to the project root. A trailing slash, a leading `./` and letter case make no difference: `important-data`, `important-data/`, `./important-data` and `Important-Data` are the same path.
|
|
255
|
+
|
|
256
|
+
`torch-it` only deletes inside the directory it is run in. A `customPaths` entry that is the project root or outside it (`.`, `..`, `../other`) stops the run.
|
|
257
|
+
|
|
258
|
+
If a target is a symbolic link with a protected path inside it, the link is left alone rather than followed.
|
|
259
|
+
|
|
260
|
+
An unknown option or a value of the wrong type, in `torchrc.json` or on the command line, stops `torch-it` before anything is deleted. A typo in `protectedPaths` should never cost you the files you meant to keep.
|
|
261
|
+
|
|
134
262
|
### Command line overrides
|
|
135
263
|
|
|
136
|
-
Any config option can be
|
|
264
|
+
Any config option can be set with a flag. For the true/false options, flags take precedence over `torchrc.json`.
|
|
137
265
|
|
|
138
266
|
```bash
|
|
139
|
-
torch-it --yes --rebuild=false --customPaths=
|
|
267
|
+
torch-it --yes --rebuild=false --customPaths=temp,logs
|
|
140
268
|
torch-it --dockerMode=true --logfile=true
|
|
141
269
|
```
|
|
142
270
|
|
|
271
|
+
A list flag adds to the list of the same name in `torchrc.json`: `--protectedPaths=coverage` protects `coverage` as well as everything the file protects. To take a path out of a list, edit the file.
|
|
272
|
+
|
|
273
|
+
List options take comma-separated paths. A JSON array works too, but most shells need it quoted: `'--customPaths=["temp","logs"]'`.
|
|
274
|
+
|
|
143
275
|
**All flags:**
|
|
144
276
|
|
|
145
277
|
| Flag | Description |
|
|
146
278
|
|------|-------------|
|
|
147
|
-
| `--help` | Show help and available options |
|
|
279
|
+
| `--help`, `-h` | Show help and available options |
|
|
280
|
+
| `--llms` | Print the reference for AI agents and scripts (`llms.txt`) |
|
|
148
281
|
| `--version`, `-v` | Show version and exit |
|
|
149
282
|
| `--config` | Show current configuration and exit |
|
|
150
283
|
| `--test` | Dry run — preview changes without executing |
|
|
151
284
|
| `--yes`, `-y` | Skip confirmation prompt |
|
|
152
|
-
| `--
|
|
153
|
-
| `--
|
|
285
|
+
| `--quiet`, `-q` | Print only warnings, errors and the final summary |
|
|
286
|
+
| `--plain` | No colour, emoji, banner or boxes. Automatic when output is not a terminal |
|
|
287
|
+
| `--json` | Print one JSON document describing the run. Needs `--yes` or `--test` |
|
|
288
|
+
| `--cwd=dir` | Run in `dir` instead of the current directory. `--cwd dir` also works |
|
|
289
|
+
| `--customPaths=a,b` | Extra paths to delete |
|
|
290
|
+
| `--only=a,b` | Remove only these paths instead of the default targets |
|
|
291
|
+
| `--protectedPaths=a,b` | Paths to preserve |
|
|
154
292
|
| `--dockerMode=true\|false` | Enable/disable Docker steps |
|
|
293
|
+
| `--dockerVolumes=true\|false` | Also remove Docker volumes in Docker mode |
|
|
294
|
+
| `--cacheClean=true\|false` | Enable/disable the package manager cache clean |
|
|
295
|
+
| `--allowTracked=true\|false` | Also remove files tracked in git, which are kept by default |
|
|
155
296
|
| `--rebuild=true\|false` | Enable/disable dependency reinstall and Docker rebuild |
|
|
156
297
|
| `--logfile=true\|false` | Enable/disable log file output |
|
|
157
298
|
|
|
@@ -170,10 +311,10 @@ React, Next.js, Vue, Vite, SvelteKit, React Native, Expo, Remix, Qwik, Nuxt, Ast
|
|
|
170
311
|
`dist`, `build`, `out`, `.output`, `.next`, `.nuxt`, `.svelte-kit`, `.svelte`, `.remix`, `.qwik`, `.astro`, `.angular`, `.angular/cache`, `.solid`, `.docusaurus`, `.nitro`
|
|
171
312
|
|
|
172
313
|
### Build tool caches
|
|
173
|
-
`.cache`, `.parcel-cache`, `.webpack`, `.rollup.cache`, `.vite`, `.swc`, `.rpt2_cache`, `.eslintcache`, `.stylelintcache`, `.sass-cache`, `.babel-cache`, `.cache-loader`
|
|
314
|
+
`.cache`, `.parcel-cache`, `.webpack`, `.rollup.cache`, `.vite`, `.vite/deps`, `.swc`, `.rpt2_cache`, `.eslintcache`, `.stylelintcache`, `.sass-cache`, `.babel-cache`, `.cache-loader`
|
|
174
315
|
|
|
175
316
|
### Package manager caches
|
|
176
|
-
`node_modules/.cache`, `.npm`, `.pnpm-store`, `.pnpm-debug.log`, `.yarn/cache`, `.yarn/unplugged`, `.yarn/install-state.gz`
|
|
317
|
+
`node_modules/.cache`, `.npm`, `.pnpm-store`, `.pnpm-debug.log`, `.yarn/cache`, `.yarn/unplugged`, `.yarn/install-state.gz`, `.yarn/build-state.yml`
|
|
177
318
|
|
|
178
319
|
### Monorepo & build tools
|
|
179
320
|
`.turbo`, `.nx/cache`, `.lerna`, `.rush`, `.yalc`
|
|
@@ -190,18 +331,37 @@ React, Next.js, Vue, Vite, SvelteKit, React Native, Expo, Remix, Qwik, Nuxt, Ast
|
|
|
190
331
|
### File patterns
|
|
191
332
|
`*.log`, `*.tgz`, `*.tar.gz`, `tsconfig.tsbuildinfo`, `coverage`, `.nyc_output`, `storybook-static`, `.storybook-out`
|
|
192
333
|
|
|
334
|
+
The `*` patterns match files in the project root only.
|
|
335
|
+
|
|
193
336
|
### Temporary files
|
|
194
|
-
`.tmp`, `tmp`, `temp`, `
|
|
337
|
+
`.tmp`, `tmp`, `temp`, `jspm_packages`, `.typings`
|
|
338
|
+
|
|
339
|
+
`lib`, `es`, `cjs` and `umd` are **not** removed by default, because they often hold hand-written source. If your project builds into them, add them to `customPaths`.
|
|
195
340
|
|
|
196
341
|
</details>
|
|
197
342
|
|
|
198
343
|
---
|
|
199
344
|
|
|
345
|
+
## Using with AI agents and scripts
|
|
346
|
+
|
|
347
|
+
[`llms.txt`](llms.txt) is the reference for AI coding agents and scripts: every option, what is deleted, how protection works, exit codes and the phrases to look for in the output. It ships in the npm package next to this README, and `torch-it --llms` prints it, so an agent can read the reference for the exact version that is installed.
|
|
348
|
+
|
|
349
|
+
The safe sequence is the same for an agent as for a person in a hurry:
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
torch-it --test --json # preview: nothing is deleted, nothing is asked
|
|
353
|
+
torch-it --yes --json # run it, once the list has been checked
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
`--json` gives the plan or the result as one JSON document (see [JSON output](#json-output)). Without it, captured output is plain text with a fixed prefix per line (see [Output](#output)), and `--quiet` cuts it down to warnings, errors and the summary.
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
200
360
|
## Logging
|
|
201
361
|
|
|
202
362
|
By default, output goes to the console only. To save a log file for troubleshooting, set `logfile: true` in `torchrc.json` or pass `--logfile=true`.
|
|
203
363
|
|
|
204
|
-
The log is written to `torch-it.log` in your project root. Add it to `.gitignore`:
|
|
364
|
+
The log is written to `torch-it.log` in your project root, and is left in place by the `*.log` cleanup while logging is on. Add it to `.gitignore`:
|
|
205
365
|
|
|
206
366
|
```gitignore
|
|
207
367
|
torch-it.log
|
|
@@ -211,7 +371,9 @@ torch-it.log
|
|
|
211
371
|
|
|
212
372
|
## Troubleshooting
|
|
213
373
|
|
|
214
|
-
**
|
|
374
|
+
**"N path(s) could not be removed":** Something still has those files open, usually a dev server, a test watcher or your editor holding `node_modules`. Stop it and run `torch-it` again.
|
|
375
|
+
|
|
376
|
+
**Docker issues:** Docker mode needs a Compose file in the project root. Run `docker compose ps` there to check if Compose is working. For rebuilds, confirm `docker compose` is on your PATH. Check that the Docker daemon is running with `docker info`.
|
|
215
377
|
|
|
216
378
|
**Missing `package.json`:** Run `npm init -y` to initialise a project, or make sure you're in the right directory.
|
|
217
379
|
|
|
@@ -219,6 +381,44 @@ torch-it.log
|
|
|
219
381
|
|
|
220
382
|
---
|
|
221
383
|
|
|
384
|
+
## Upgrading from 2.x
|
|
385
|
+
|
|
386
|
+
Version 3 closes several ways a run could delete more than intended. If you used 2.x, these are the changes you may notice:
|
|
387
|
+
|
|
388
|
+
| Change | What to do |
|
|
389
|
+
|--------|------------|
|
|
390
|
+
| With no terminal (CI, pipes) and no `--yes`, it stops with exit code `1` instead of deleting unprompted | Add `--yes` to automated runs |
|
|
391
|
+
| Answering no at the prompt exits with `3`, not `0` | Check scripts that wrap an interactive run |
|
|
392
|
+
| An invalid `torchrc.json`, an unknown option or a value of the wrong type stops the run. Before, it was ignored | Fix the option it names |
|
|
393
|
+
| `lib`, `es`, `cjs` and `umd` are no longer deleted by default | Add them to `customPaths` if your build writes to them |
|
|
394
|
+
| Only the cache of the package manager your project uses is cleaned, not every one installed | Nothing |
|
|
395
|
+
| Exit code is `2` when a path can not be removed, the dependency install fails, or Docker mode is on and Docker is unavailable or a Docker step fails | Check scripts that assumed `0` |
|
|
396
|
+
| A `customPaths` entry that is the project root or outside it is refused | Run `torch-it` from the directory you want cleaned |
|
|
397
|
+
| Protected paths are matched without regard to case | Nothing |
|
|
398
|
+
| Files tracked in git are kept, even inside a target such as `build` | Pass `--allowTracked=true` if you really commit your build output and want it wiped |
|
|
399
|
+
| Docker mode no longer removes volumes unless `dockerVolumes` is `true` | Set it if you relied on volumes being wiped |
|
|
400
|
+
| Docker mode needs a Compose file; a `Dockerfile` alone is skipped. `compose.yaml` and `compose.yml` are now recognised | Nothing |
|
|
401
|
+
| `*.log`, `*.tgz` and `*.tar.gz` match those extensions only. Before, `*.log` also caught root files such as `catalog.json` | Nothing |
|
|
402
|
+
| List flags take comma-separated paths: `--customPaths=temp,logs` | The quoted JSON form still works |
|
|
403
|
+
| Output that is not going to a terminal is plain text, and warnings and errors go to stderr | Update anything that matched the old emoji or read errors from stdout |
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## Development
|
|
408
|
+
|
|
409
|
+
```bash
|
|
410
|
+
npm install
|
|
411
|
+
npm run torch-it:ts -- --test # run from source with tsx
|
|
412
|
+
npm run quality # lint, format check, fallow and tests
|
|
413
|
+
npm run build # bundle to dist/torch-it.js
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
- `npm run fallow` checks for unused code, duplication and over-complex functions. It runs in the pre-push hook and in CI.
|
|
417
|
+
- Branches are named `feat/…`, `fix/…`, `hotfix/…`, `release/…` or `chore/…`, and commits follow [Conventional Commits](https://www.conventionalcommits.org/). Both are checked by hooks and in CI.
|
|
418
|
+
- Open pull requests against `dev`. `dev` is merged into `main` for a release.
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
222
422
|
## License
|
|
223
423
|
|
|
224
424
|
MIT — **Happy torching!** 🔥✨
|
package/dist/torch-it.js
CHANGED
|
@@ -1,34 +1,46 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var
|
|
3
|
-
`)};
|
|
4
|
-
|
|
2
|
+
"use strict";var Tt=Object.create;var Ce=Object.defineProperty;var Et=Object.getOwnPropertyDescriptor;var Pt=Object.getOwnPropertyNames;var Dt=Object.getPrototypeOf,Ot=Object.prototype.hasOwnProperty;var wt=(e,t)=>()=>{try{return t||e((t={exports:{}}).exports,t),t.exports}catch(o){throw t=0,o}};var At=(e,t,o,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of Pt(t))!Ot.call(e,s)&&s!==o&&Ce(e,s,{get:()=>t[s],enumerable:!(n=Et(t,s))||n.enumerable});return e};var f=(e,t,o)=>(o=e!=null?Tt(Dt(e)):{},At(t||!e||!e.__esModule?Ce(o,"default",{value:e,enumerable:!0}):o,e));var Re=wt((bo,xt)=>{xt.exports={name:"torch-it",version:"3.0.0",description:"CLI to reset, clean, and rebuild Node.js project environments.",keywords:["cleanup","cli","docker","node"],homepage:"https://github.com/piyook/torch-it#readme",bugs:{url:"https://github.com/piyook/torch-it/issues"},license:"MIT",author:"piyook",repository:{type:"git",url:"git+https://github.com/piyook/torch-it.git"},bin:{"torch-it":"dist/torch-it.js"},files:["dist","README.md","llms.txt"],type:"commonjs",main:"dist/torch-it.js",engines:{node:">=20"},scripts:{build:'esbuild src/torch.ts --bundle --outfile=dist/torch-it.js --platform=node --format=cjs --target=node20 --minify --banner:js="#!/usr/bin/env node"',"torch-it":"npm run build && node dist/torch-it.js","torch-it:ts":"tsx src/torch.ts",lint:"oxlint .","lint:fix":"oxlint . --fix",format:'prettier --check "src/**/*.{ts,js,json}" "tests/**/*.{ts,js,json}" package.json package-lock.json .commitlintrc.json',"format:write":'prettier --write "src/**/*.{ts,js,json}" "tests/**/*.{ts,js,json}" package.json package-lock.json .commitlintrc.json',fallow:"fallow",quality:"npm run lint && npm run format && npm run fallow && npm test","validate-branch-name":"validate-branch-name",prepare:"husky",prepublishOnly:"npm run build",test:"tsc --noEmit && vitest run","test:watch":"vitest"},devDependencies:{"@commitlint/cli":"^21.2.3","@commitlint/config-conventional":"^21.2.3","@types/node":"^26.6.4",esbuild:"^0.28.2",fallow:"^3.33.0",husky:"^9.1.7","lint-staged":"^17.6.0",oxlint:"^1.87.0",prettier:"^3.9.9",tsx:"^4.23.15",typescript:"^7.0.2","validate-branch-name":"^1.3.2",vitest:"^5.0.3"},"lint-staged":{"*.{ts,tsx,js,jsx,mjs,cjs}":["oxlint --fix","prettier --write"],"*.json":"prettier --write"},"validate-branch-name":{pattern:"^(main|dev){1}$|^(feat|fix|hotfix|release|chore)/.+$",errorMsg:"INVALID BRANCH NAME: use format 'feat|fix|hotfix|release|chore/your-branch-name'"}}});var Q=!0,ke=e=>{Q=e},E=(e,t)=>o=>Q?`\x1B[${e}m${o}\x1B[${t}m`:o,u={BOLD:E("1","22"),CYAN:E("36","39"),GREEN:E("32","39"),RED:E("31","39"),YELLOW:E("33","39"),PURPLE:E("35","39"),RESET:e=>Q?`\x1B[0m${e}`:e},p={OK:0,ERROR:1,STEP_FAILED:2,CANCELLED:3},i={INFO:"\u{1F525}",SUCCESS:"\u2705",FAIL:"\u274C",EXCLAMATION:"\u2757",WARN:"\u26A0\uFE0F",ROCKET:"\u{1F525}",CLEAN:"\u{1F525}",PHOENIX:"\u{1F525}",BUILD:"\u{1F528}",TARGET:"\u{1F525}",CELEBRATE:"\u{1F525}",STARS:"\u2728",BOX:"\u{1F4E6}",CLIPBOARD:"\u{1F4CB}",DOCKER:"\u{1F433}",WAIT:"\u23F3"};var z=f(require("fs")),F="torch-it.log",Z=!1;function vt(e){let t=new RegExp("\x1B\\[[0-9;]*m","g");return e.replace(t,"")}var P=e=>{Z=e},be=()=>{Z&&z.default.writeFileSync(F,"")},D=e=>{if(!Z)return;let t=vt(e).replace(/^\n/,""),o=new Date().toISOString();z.default.appendFileSync(F,`[${o}] ${t}
|
|
3
|
+
`)};var T=()=>Re().version;var ee=!1,te=[],oe=[],ye=e=>{ee=e},g=()=>ee;function Te(e,t){e==="warn"&&te.push(t),e==="fail"&&oe.push(t)}function O(e){process.stdout.write(`${JSON.stringify(e,null,2)}
|
|
4
|
+
`)}function m(){ee&&O({version:T(),ok:!1,exitCode:p.ERROR,changed:!1,warnings:te,errors:oe}),process.exit(p.ERROR)}var Lt={NO_DOCKER:"skipped",DOCKER_UNAVAILABLE:"unavailable",DOCKER_FAIL:"failed",OK:"ok"},L=e=>e?"ok":"failed";function St(e){let t=Lt[e.dockerClean],o=e.rebuild!==!1&&(t==="ok"||t==="failed");return{dockerTeardown:t,dockerRebuild:o?L(e.dockerRebuild):"skipped",dockerStart:o&&e.dockerRebuild?L(e.dockerLaunch):"skipped"}}function Ee(e,t){let o=St(e);return{version:T(),ok:t===p.OK,exitCode:t,dryRun:e.dryRun===!0,changed:e.dryRun!==!0,cwd:process.cwd(),packageManager:e.packageManager??null,removed:e.paths?.removed??[],failed:e.paths?.failed??[],keptTracked:e.paths?.tracked??[],steps:{dockerTeardown:o.dockerTeardown,cleanup:L((e.cleanupFailures??0)===0),cacheClean:e.cacheClean===!1?"skipped":L(e.packageManagerClean),install:e.rebuild===!1?"skipped":L(e.dependencyInstall),dockerRebuild:o.dockerRebuild,dockerStart:o.dockerStart},warnings:te,errors:oe}}var w=!1,R=!1,_t={success:"ok: ",warn:"warning: ",fail:"error: ",step:"> "};function De(e){w=e.plain===!0||g()||process.stdout.isTTY!==!0,R=e.quiet===!0,ke(!w&&!process.env.NO_COLOR)}var Oe=()=>R;function j(e){let t=R;R=!1;try{e()}finally{R=t}}var $t=new RegExp(`(?:${[...new Set(Object.values(i))].join("|")})\\uFE0F? ?`,"gu"),M=e=>e.replace($t,"").trimEnd();function Nt(e,t){switch(t){case"info":return`${u.CYAN(i.INFO)} ${u.BOLD(e)}${u.RESET("")}`;case"success":return`${u.GREEN(i.SUCCESS)} ${e}${u.RESET("")}`;case"warn":return`${u.YELLOW(i.WARN)} ${e}${u.RESET("")}`;case"fail":return`${u.RED(i.FAIL)} ${e}${u.RESET("")}`;case"step":return`
|
|
5
|
+
${u.PURPLE("\u25B6")} ${u.BOLD(e)}${u.RESET("")}`;default:return e}}var It=(e,t)=>`${_t[t]??""}${M(e).replace(/^\n+/,"")}`;function r(e,t){let o=w?It(e,t):Nt(e,t);D(o),Te(t,M(e).replace(/^\n+/,"")),t==="warn"||t==="fail"?console.error(o):!R&&!g()&&console.log(o)}function we(){if(w||R)return;K([` ${i.TARGET} TORCH LIT ${i.ROCKET}`],u.PURPLE);let e=`
|
|
5
6
|
) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) )
|
|
6
7
|
(( (( (( (( (( (( (( (( (( (( (( (( (( ((
|
|
7
8
|
) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) )
|
|
8
9
|
(( (( (( (( (( (( (( (( (( (( (( (( (( ((
|
|
9
10
|
) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) )
|
|
10
|
-
____ _ _ ____ _ _ ___ _ _ ____
|
|
11
|
+
____ _ _ ____ _ _ ___ _ _ ____
|
|
11
12
|
| __ )| | | | _ \\| \\ | |_ _| \\ | |/ ___|
|
|
12
|
-
| _ \\| | | | |_) | \\| || || \\| | | _
|
|
13
|
+
| _ \\| | | | |_) | \\| || || \\| | | _
|
|
13
14
|
| |_) | |_| | _ <| |\\ || || |\\ | |_| |
|
|
14
15
|
|____/ \\___/|_| \\_\\_| \\_|___|_| \\_|\\____|
|
|
15
|
-
`;console.log(e),
|
|
16
|
+
`;console.log(e),D(e)}function Ae(){w||R||K([` ${i.PHOENIX} RISING FROM THE ASHES ${i.PHOENIX}`],u.PURPLE)}function Pe(e){let t=new RegExp("\x1B\\[[0-9;]*m","g");return e.replace(t,"").length}function K(e,t=u.GREEN){if(g()){e.forEach(s=>D(M(s)));return}if(w){e.map(M).filter(s=>s.trim()!=="").forEach(s=>{D(s),console.log(s)});return}console.log("");let o=Math.max(56,...e.map(s=>Pe(s))),n=t("\u2550".repeat(o));console.log(t("\u2554"+n+"\u2557")),e.forEach(s=>{if(D(s),s.trim()==="")console.log(t("\u2551"+" ".repeat(o)+"\u2551"));else{let c=o-Pe(s);console.log(t("\u2551"+s+" ".repeat(c)+"\u2551"))}}),console.log(t("\u255A"+n+"\u255D"))}var U=f(require("fs"));var ve={customPaths:[],customDirs:[],customFiles:[],protectedPaths:[],only:[],dockerMode:!1,dockerVolumes:!1,logfile:!1,rebuild:!0,cacheClean:!0,allowTracked:!1};var k=f(require("fs")),y=f(require("path"));var re=e=>y.relative(process.cwd(),y.resolve(e.trim().replace(/\\/g,y.sep))).replace(/\\/g,"/"),ne=e=>re(e).toLowerCase(),xe=e=>{let t=e.split("/");return t.slice(0,-1).map((o,n)=>t.slice(0,n+1).join("/"))};function S(e){let t=new Set(e.map(ne)),o=new Set;for(let n of t)n!==""&&o.add(""),xe(n).forEach(s=>o.add(s));return{covers:n=>{let s=ne(n);return t.has("")||t.has(s)||xe(s).some(c=>t.has(c))},holds:n=>o.has(ne(n))}}function se(e){let t=re(e);return t!==""&&t!==".."&&!t.startsWith("../")&&!y.isAbsolute(t)}function A(e,t){let o=S(t);return e.filter(n=>!o.covers(n))}function _(e){return e.filter(t=>k.existsSync(t))}function Ft(e){let t=e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*");return new RegExp(`^${t}$`)}function ie(e){let t=Ft(e);return k.readdirSync(".",{withFileTypes:!0}).filter(o=>o.isFile()&&t.test(o.name)).map(o=>o.name)}function Le(e){let{isDryRun:t,protectedPaths:o}=e,n=S(o),s=S(_(o)),c=[],l=[],C=d=>{if(t){j(()=>r(`Would remove ${d}`,"info")),c.push(d);return}r(`Removing ${d}...`,"step");try{k.rmSync(d,{recursive:!0,force:!0}),r(`${d} removed`,"success"),c.push(d)}catch{r(`Failed to remove ${d}`,"fail"),l.push(d)}},I=d=>{if(k.lstatSync(d).isSymbolicLink()){r(`Skipping ${d}: it is a link and a protected path lies inside it`,"warn");return}for(let yt of k.readdirSync(d))he(`${re(d)}/${yt}`)},he=d=>{if(!(!k.existsSync(d)||n.covers(d))){if(!se(d)){r(`Skipping ${d}: not inside the project`,"warn");return}if(!s.holds(d)){C(d);return}try{I(d)}catch{r(`Failed to read ${d}`,"fail"),l.push(d)}}};return{removedPaths:()=>c,failedPaths:()=>l,cleanupTarget:he}}function Se(e,t){try{ie(e).forEach(t.cleanupTarget)}catch(o){r(`Failed to process pattern ${e}: ${o}`,"warn")}}var $="torchrc.json",_e=["customPaths","customDirs","customFiles","protectedPaths","only"],Mt=["dockerMode","dockerVolumes","logfile","rebuild","cacheClean","allowTracked"],jt=["customPaths","customDirs","customFiles","only"],$e=e=>_e.includes(e),Ne=e=>Mt.includes(e),Kt=e=>e.map(t=>t.trim()).filter(t=>t.length>0),Ie=e=>Array.isArray(e)&&e.every(t=>typeof t=="string"),Ut=(e,t,o,n,s)=>{if(!Ie(o)){s.push(`${n} must be an array of strings`);return}let c=Kt(o);jt.includes(t)&&c.filter(l=>!se(l)).forEach(l=>s.push(`${n} entry "${l}" is the project root or outside it`)),e[t]=c},Fe=(e,t,o,n,s)=>{$e(t)?Ut(e,t,o,n,s):Ne(t)?typeof o=="boolean"?e[t]=o:s.push(`${n} must be true or false`):s.push(`Unknown option ${n}`)},Ht=e=>{try{let t=JSON.parse(e);if(Ie(t))return t}catch{}return e.replace(/^\[|\]$/g,"").split(",").map(t=>t.trim().replace(/^["']|["']$/g,""))},Bt=(e,t)=>Ne(e)?t===void 0||t==="true"?!0:t==="false"?!1:t:$e(e)&&t?Ht(t):t,Yt=(e,t)=>{let o={};for(let n of e){if(!n.startsWith("--")){t.push(`Unknown argument ${n}`);continue}let s=n.indexOf("="),c=s===-1?n.slice(2):n.slice(2,s),l=s===-1?void 0:n.slice(s+1);Fe(o,c,Bt(c,l),`--${c}`,t)}return o},Gt=e=>{if(!U.existsSync($))return{};let t;try{t=JSON.parse(U.readFileSync($,"utf8"))}catch{return e.push(`${$} is not valid JSON`),{}}if(typeof t!="object"||t===null||Array.isArray(t))return e.push(`${$} must contain a JSON object`),{};let o={};for(let[n,s]of Object.entries(t))Fe(o,n,s,`${$} "${n}"`,e);return o},Wt=(e,t)=>{let o={};for(let n of _e)o[n]=[...new Set([...e[n]??[],...t[n]??[]])];return o},Vt=(e=[])=>{let t=[],o=Gt(t),n=Yt(e,t);return{config:{...ve,...o,...n,...Wt(o,n)},problems:t}},H=(e=[])=>{let{config:t,problems:o}=Vt(e);return o.length>0&&(o.forEach(n=>r(n,"fail")),r("Nothing was changed. Fix the above or run 'torch-it --help'.","info"),m()),t},B=e=>[...new Set([...e.customPaths,...e.customDirs,...e.customFiles])],Me=e=>e.logfile?[...e.protectedPaths,F]:e.protectedPaths;var je=()=>{r(`
|
|
16
17
|
\u{1F525} Torch It - Project Environment Reset Tool
|
|
17
18
|
|
|
18
19
|
USAGE:
|
|
19
20
|
torch-it [options]
|
|
20
21
|
|
|
21
22
|
OPTIONS:
|
|
22
|
-
--help
|
|
23
|
+
--help, -h Show this help message and exit
|
|
24
|
+
--llms Print the full reference for AI agents and scripts (llms.txt)
|
|
23
25
|
--version, -v Show version information and exit
|
|
24
26
|
--config Show current configuration and exit
|
|
25
27
|
--test Run in dry-run mode (preview changes without executing)
|
|
26
|
-
--yes, -y Skip the confirmation prompt (
|
|
27
|
-
--
|
|
28
|
+
--yes, -y Skip the confirmation prompt (required when there is no terminal, e.g. CI)
|
|
29
|
+
--quiet, -q Print only warnings, errors and the final summary
|
|
30
|
+
--plain No colour, emoji, banner or boxes (automatic when output is not a terminal)
|
|
31
|
+
--json Print one JSON document describing the run (needs --yes or --test)
|
|
32
|
+
--cwd=<dir> Run in <dir> instead of the current directory
|
|
33
|
+
--dockerMode=<bool> Enable/disable Docker Compose operations (default: false)
|
|
34
|
+
--dockerVolumes=<bool> Also remove Docker volumes in Docker mode (default: false)
|
|
28
35
|
--rebuild=<bool> Enable/disable rebuild operations (default: true)
|
|
36
|
+
--cacheClean=<bool> Enable/disable the package manager cache clean (default: true)
|
|
37
|
+
--only=<list> Remove only these paths instead of the default targets
|
|
38
|
+
--allowTracked=<bool> Also remove files tracked in git (default: false, they are kept)
|
|
29
39
|
--logfile=<bool> Enable/disable file logging (default: false)
|
|
30
|
-
--customPaths=<
|
|
31
|
-
--protectedPaths=<
|
|
40
|
+
--customPaths=<list> Additional paths to remove during cleanup (comma-separated)
|
|
41
|
+
--protectedPaths=<list> Paths to skip during cleanup (comma-separated)
|
|
42
|
+
|
|
43
|
+
Lists given here are added to the ones in torchrc.json.
|
|
32
44
|
|
|
33
45
|
EXAMPLES:
|
|
34
46
|
torch-it # Run with default settings
|
|
@@ -39,28 +51,51 @@ EXAMPLES:
|
|
|
39
51
|
torch-it --rebuild=false # Clean only, don't rebuild
|
|
40
52
|
torch-it --dockerMode=true # Enable Docker cleanup/rebuild when configured
|
|
41
53
|
torch-it --logfile=true # Enable file logging
|
|
42
|
-
torch-it --customPaths=
|
|
54
|
+
torch-it --customPaths=temp,logs # Clean additional paths
|
|
55
|
+
torch-it --protectedPaths=dist # Keep a path that would otherwise be removed
|
|
56
|
+
torch-it --only=node_modules --cacheClean=false # Just reinstall dependencies
|
|
57
|
+
torch-it --cwd=apps/web --test # Preview another directory
|
|
58
|
+
torch-it --test --json # The plan, as JSON
|
|
59
|
+
|
|
60
|
+
OUTPUT:
|
|
61
|
+
Warnings and errors go to stderr, everything else to stdout.
|
|
62
|
+
Set NO_COLOR to turn colour off and keep the rest.
|
|
63
|
+
|
|
64
|
+
EXIT CODES:
|
|
65
|
+
0 Every step succeeded (or nothing to do)
|
|
66
|
+
1 Nothing was changed: invalid options, not a Node.js project,
|
|
67
|
+
or no terminal to confirm on and no --yes
|
|
68
|
+
2 The run went ahead and a step failed (a path could not be removed,
|
|
69
|
+
install failed, Docker unavailable or failed)
|
|
70
|
+
3 You answered no at the prompt
|
|
43
71
|
|
|
44
72
|
CONFIGURATION:
|
|
45
73
|
Create a torchrc.json file in your project root for persistent settings:
|
|
46
|
-
|
|
74
|
+
|
|
47
75
|
{
|
|
48
76
|
"customPaths": ["apps/web/.next", "coverage-final.json"],
|
|
49
77
|
"protectedPaths": ["important-data/"],
|
|
50
78
|
"dockerMode": false,
|
|
79
|
+
"dockerVolumes": false,
|
|
51
80
|
"rebuild": true,
|
|
81
|
+
"cacheClean": true,
|
|
52
82
|
"logfile": false
|
|
53
83
|
}
|
|
54
84
|
|
|
85
|
+
AI AGENTS AND SCRIPTS:
|
|
86
|
+
Run 'torch-it --llms' for the full reference, and use --test --json
|
|
87
|
+
to get the plan before --yes --json.
|
|
88
|
+
|
|
55
89
|
For more information, visit: https://github.com/piyook/torch-it
|
|
56
|
-
`.trim(),"info")};var
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
PROTECTED PATHS (will NOT be deleted):`,
|
|
62
|
-
PROTECTED PATHS: None
|
|
63
|
-
CUSTOM PATHS:`,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
90
|
+
`.trim(),"info")};var v=f(require("fs"));var Ke=["node_modules","dist","build",".svelte-kit",".svelte",".next",".nuxt","out",".output",".vercel",".netlify",".astro",".angular",".angular/cache",".solid",".docusaurus",".vite",".cache",".parcel-cache",".webpack",".rollup.cache",".rpt2_cache",".swc",".eslintcache",".stylelintcache",".sass-cache",".tmp","tmp","temp","coverage",".nyc_output","storybook-static",".storybook-out",".turbo",".rush",".firebase",".serverless","android/.gradle","android/build","android/app/build","ios/Pods","ios/build",".expo",".expo-shared",".remix",".qwik",".nitro",".wrangler",".amplify",".sst",".babel-cache",".cache-loader","jspm_packages",".typings"],Ue=["*.log","*.tgz","*.tar.gz","tsconfig.tsbuildinfo"],He=[".pnpm-store",".pnpm-debug.log",".yarn/cache",".yarn/unplugged",".yarn/install-state.gz",".yarn/build-state.yml",".npm","node_modules/.cache",".yalc",".lerna",".nx/cache",".vite/deps",".turbo","tsconfig.tsbuildinfo",".hardhat",".foundry",".anchor"],Be=[];var Ye=require("child_process");function Ge(e){if(e.length===0)return[];try{return(0,Ye.execFileSync)("git",["ls-files","-z","--",...e],{encoding:"utf8",stdio:["ignore","pipe","ignore"],maxBuffer:268435456}).split("\0").filter(o=>o.length>0)}catch{return[]}}function Y(e){return e.only.length>0?{onlyMode:!0,defaultTargets:[],filePatterns:[],customTargets:[...new Set(e.only)]}:{onlyMode:!1,defaultTargets:[...new Set([...Ke,...He,...Be])],filePatterns:[...Ue],customTargets:B(e)}}var qt=e=>{let t=e.replace(/\\/g,"/").replace(/\/+$/,"");return t.includes("*")?`:(glob)${t}`:`:(literal)${t}`};function G(e,t){let o=Me(e);if(e.allowTracked)return{protectedPaths:o,trackedFiles:[]};let n=_(Ge([...t.defaultTargets,...t.filePatterns,...t.customTargets].map(qt)));return{protectedPaths:[...o,...n],trackedFiles:n}}var W=e=>[...new Set(e.map(t=>t.split("/")[0]))].join(", ");var ae=f(require("fs"));var V=require("child_process"),We=f(require("os"));var Jt=()=>Oe()?["inherit","ignore","inherit"]:g()?["inherit",2,"inherit"]:"inherit";function b(e,t={}){try{return(0,V.execSync)(e,{stdio:t.silent?"pipe":Jt()}),!0}catch{return t.silent||r(`Command failed: ${e}`,"fail"),!1}}function N(e){try{return We.platform()==="win32"?(0,V.execSync)(`where ${e}`,{stdio:"ignore"}):(0,V.execSync)(`command -v ${e}`,{stdio:"ignore"}),!0}catch{return!1}}var Ve=["compose.yaml","compose.yml","docker-compose.yaml","docker-compose.yml"],qe=["Dockerfile",...Ve];function Je(){return Ve.some(e=>ae.existsSync(e))}function Xe(e){return N("docker")?b("docker info",{silent:!0})?!0:(r(`Docker daemon is not running - skipping Docker ${e}`,"warn"),!1):(r(`Docker is not installed - skipping Docker ${e}`,"warn"),!1)}function Qe(e){if(e.dockerMode===!1)return r("Docker mode disabled - skipping Docker operations","info"),"NO_DOCKER";let t=process.env.TORCH_DRY_RUN==="1";if(!Je())return r(ae.existsSync("Dockerfile")?"Dockerfile found but no Compose file - Docker mode needs a Compose file, skipping Docker operations":"No Docker configuration found - skipping Docker operations","info"),"NO_DOCKER";if(r("Stopping Docker services and removing all resources...","info"),!Xe("cleanup"))return"DOCKER_UNAVAILABLE";if(!b("docker compose ps",{silent:!0}))return r("docker compose could not read this project's Compose file - skipping Docker cleanup","fail"),"DOCKER_FAIL";let o=`docker compose down --rmi all${e.dockerVolumes?" --volumes":""}`;return t?(r(`Dry-run: would run '${o}'`,"info"),"OK"):b(o)?(r("Docker services stopped and resources cleaned","success"),"OK"):(r("Docker cleanup encountered issues","warn"),r("Continuing with the rest of the script...","info"),"DOCKER_FAIL")}function ze(e){if(e.dockerMode===!1)return!1;if(e.rebuild===!1)return r("Rebuild disabled - skipping Docker rebuild","info"),!1;let t=process.env.TORCH_DRY_RUN==="1";return Je()?Xe("rebuild")?(r("Building Docker resources (this may take a while...)...","step"),r(`${i.WAIT} Please be patient - pulling fresh images and building...`,"info"),t?(r("Dry-run: would run 'docker compose build --pull --no-cache'","info"),!0):b("docker compose build --pull --no-cache")?!0:(r("Docker build failed - see the output above","fail"),!1)):(r("Run 'docker compose build --pull --no-cache' once Docker is available","info"),!1):!1}function Ze(e){if(e.dockerMode===!1)return!1;let t=process.env.TORCH_DRY_RUN==="1";return r("Starting Docker services in detached mode...","step"),t?(r("Dry-run: would run 'docker compose up -d'","info"),!0):b("docker compose up -d")?!0:(r("Failed to start Docker services - see the output above","fail"),!1)}var ce=f(require("fs"));var q={npm:{name:"npm",lockFile:"package-lock.json",installCommand:"npm install",cacheCleanCommand:"npm cache clean --force"},yarn:{name:"yarn",lockFile:"yarn.lock",installCommand:"yarn install",cacheCleanCommand:"yarn cache clean"},pnpm:{name:"pnpm",lockFile:"pnpm-lock.yaml",installCommand:"pnpm install",cacheCleanCommand:"pnpm store prune"}};function J(){for(let e of["pnpm","yarn","npm"])if(ce.existsSync(q[e].lockFile)&&N(e))return e;for(let e of["npm","yarn","pnpm"])if(N(e))return e;return null}function le(e){let{lockFile:t}=q[e];return ce.existsSync(t)?`${e} (detected ${t})`:`${e} (fallback)`}function et(e){let t=process.env.TORCH_DRY_RUN==="1",o=q[e];return r(`Using ${le(e)}...`,"step"),t||b(o.installCommand)?(r(`Dependencies ${t?"would be installed":"installed successfully"} with ${e}`,"success"),!0):(r(`${e} install failed`,"fail"),!1)}function tt(e){let t=process.env.TORCH_DRY_RUN==="1",o=q[e];return t||b(o.cacheCleanCommand)?(r(t?`Dry-run: would clean ${e} cache`:`${e} cache cleaned`,"success"),!0):!1}var a=e=>r(e,"info"),nt=e=>v.existsSync(e)?v.statSync(e).isDirectory()?"(DIR)":"(FILE)":"(not found)";function Xt(){a(`
|
|
91
|
+
PACKAGE MANAGER:`);let e=J();e?a(` ${le(e)}`):r(" No package manager detected","warn")}function Qt(){a(`
|
|
92
|
+
DOCKER DETECTION:`);let e=qe.filter(t=>v.existsSync(t));e.length>0?e.forEach(t=>a(` ${t}`)):a(" No Docker configuration found")}function zt(e,t){try{let o=A(ie(e),t);if(o.length===0){a(` ${e} (no matches)`);return}a(` ${e} (matches ${o.length} file${o.length===1?"":"s"}):`),o.forEach(n=>a(` ${n} (FILE)`))}catch(o){r(` ${e} (error: ${o})`,"warn")}}function ot(e,t,o){if(t.length===0)return;a(e);let n=S(_(o));t.forEach(s=>{let c=n.holds(s)?" - protected contents kept":"";a(` ${s} ${nt(s)}${c}`)})}function Zt(e,t){a(`
|
|
93
|
+
FILES/DIRECTORIES THAT WILL BE DELETED:`);let o=l=>A(l,t).filter(C=>v.existsSync(C)),n=o([...e.defaultTargets,...e.filePatterns.filter(l=>!l.includes("*"))]),s=e.filePatterns.filter(l=>l.includes("*")),c=o(e.customTargets);ot(" Default targets:",n,t),s.length>0&&(a(" Glob patterns:"),s.forEach(l=>zt(l,t))),ot(e.onlyMode?" Only these (nothing else is removed):":" Custom targets:",c,t),n.length===0&&s.length===0&&c.length===0&&a(" No targets found (nothing to delete)")}function de(e,t){a(e),t.forEach(o=>a(` ${o} ${nt(o)}`))}function ue(e,t){let o=t?.includeHelpFooter!==!1,n=B(e),s=e.protectedPaths;a("=".repeat(60)),a("TORCH-IT CONFIGURATION"),a("=".repeat(60)),a(`
|
|
94
|
+
BASIC SETTINGS:`),a(` Docker Mode: ${e.dockerMode}`),a(` Docker Volumes: ${e.dockerVolumes}`),a(` Cache Clean: ${e.cacheClean}`),a(` Allow Tracked: ${e.allowTracked}`),a(` Rebuild: ${e.rebuild}`),a(` Logfile: ${e.logfile}`),Xt(),Qt();let c=Y(e),l=G(e,c);Zt(c,l.protectedPaths),l.trackedFiles.length>0&&a(` Kept: ${l.trackedFiles.length} file(s) tracked in git, under: ${W(l.trackedFiles)}`),s.length>0?de(`
|
|
95
|
+
PROTECTED PATHS (will NOT be deleted):`,s):a(`
|
|
96
|
+
PROTECTED PATHS: None`),n.length>0&&de(`
|
|
97
|
+
CUSTOM PATHS:`,n),e.only.length>0&&de(`
|
|
98
|
+
ONLY (replaces the default targets):`,e.only),a(`
|
|
99
|
+
`+"=".repeat(60)),o&&(a("Use 'torch-it --help' for available options"),a("=".repeat(60)))}var rt=e=>{ue(e)};var X=f(require("fs")),pe=f(require("path")),st="llms.txt",eo=()=>[pe.join(__dirname,"..",st),pe.join(__dirname,"..","..",st)].find(e=>X.existsSync(e)),it=()=>{let e=eo();return e?X.readFileSync(e,"utf8"):void 0};var to={"--help":"isHelp","-h":"isHelp","--llms":"isLlms","--version":"isVersion","-v":"isVersion","--config":"isConfig","--test":"isDryRun","--yes":"assumeYes","-y":"assumeYes","--quiet":"isQuiet","-q":"isQuiet","--plain":"isPlain","--json":"isJson"};function at(e){let t={isHelp:!1,isLlms:!1,isVersion:!1,isConfig:!1,isDryRun:!1,assumeYes:!1,isQuiet:!1,isPlain:!1,isJson:!1,filteredArgs:[]};for(let o=0;o<e.length;o++){let n=e[o],s=to[n];s?t[s]=!0:n==="--cwd"?t.cwd=e[++o]??"":n.startsWith("--cwd=")?t.cwd=n.slice(6):t.filteredArgs.push(n)}return t}function ct(e){if(e.cwd!==void 0)try{process.chdir(e.cwd)}catch{r(e.cwd===""?"--cwd needs a directory":`--cwd: cannot use "${e.cwd}" as the working directory`,"fail"),m()}}function lt(e){if(e.isHelp&&(P(!1),je(),process.exit(p.OK)),e.isLlms){let t=it();t===void 0?(r("llms.txt is missing from this install. Read it at https://github.com/piyook/torch-it/blob/main/llms.txt","fail"),m()):process.stdout.write(t),process.exit(p.OK)}if(e.isVersion&&(P(!1),g()?O({version:T()}):r(`torch-it v${T()}`,"info"),process.exit(p.OK)),e.isConfig){P(!1);let t=H(e.filteredArgs);g()?O({version:T(),cwd:process.cwd(),config:t}):rt(t),process.exit(p.OK)}e.isDryRun&&(process.env.TORCH_DRY_RUN="1")}var oo=(e,t)=>{if(e.length!==0){r("Scanning for log files and temporary files...","step");for(let o of e)o.includes("*")?Se(o,t):t.cleanupTarget(o)}},no=(e,t)=>{let o=e.removedPaths().length,n=e.failedPaths().length;return n>0?r(`${n} path(s) could not be removed - close anything using them and run again`,"fail"):o===0&&r("No build artifacts or cache directories found (project already clean)","info"),o>0&&r(`${t?"Would remove":"Removed"} ${o} build/cache item(s)`,"success"),{cleaned:o>0,failed:n}},dt=e=>{let t=process.env.TORCH_DRY_RUN==="1",o=Y(e),{protectedPaths:n,trackedFiles:s}=G(e,o),c=A(o.defaultTargets,n),l=A(o.customTargets,n),C=Le({isDryRun:t,protectedPaths:n});o.onlyMode||(r("Scanning for build artifacts and cache directories...","step"),c.forEach(C.cleanupTarget)),oo(o.filePatterns,C),l.length>0&&(r(o.onlyMode?"Removing only the paths listed in 'only'":"Deleting user defined custom directories and files","step"),l.forEach(C.cleanupTarget));let I=o.defaultTargets.length-c.length+(o.customTargets.length-l.length);return I>0&&r(`Skipped ${I} protected path(s)`,"info"),s.length>0&&r(`Kept ${s.length} file(s) tracked in git, under: ${W(s)}. Pass --allowTracked=true to remove them too.`,"warn"),{...no(C,t),tracked:s.length,paths:{removed:C.removedPaths(),failed:C.failedPaths(),tracked:s}}},ut=e=>e?tt(e):(r("No package manager cache cleaned (npm/yarn/pnpm not available)","info"),!1);var pt=e=>(r("Detecting package manager and installing dependencies...","step"),e?et(e):(r("No package manager found (npm/yarn/pnpm). Please install dependencies manually.","fail"),!1));var ro={removed:"would be removed",cleaned:"would be cleaned","freshly installed":"would be installed","rebuilt from scratch":"would be rebuilt","running in detached mode":"would be started"},x=(e,t)=>e.dryRun?ro[t]:t,ft=e=>e.cleanupFailures??0,so=(e,t)=>e.dockerClean==="DOCKER_FAIL"||e.dockerClean==="DOCKER_UNAVAILABLE"?!0:e.dockerClean==="OK"&&t&&!(e.dockerRebuild&&e.dockerLaunch),fe=e=>{let t=e.rebuild!==!1;return ft(e)>0||t&&!e.dependencyInstall?!0:so(e,t)},io=e=>{let t=ft(e);if(t>0)return[`${i.FAIL} ${t} path(s) could not be removed (in use or locked?)`];let o=e.trackedKept??0;return[e.buildAndCacheClean?`${i.CLEAN} All build artifacts & caches ${x(e,"removed")}`:`${i.STARS} No build artifacts found (already clean)`,...o>0?[`${i.WARN} ${o} file(s) tracked in git were kept`]:[]]},ao=e=>e.cacheClean===!1?`${i.CLEAN} Package manager cache left alone (cacheClean disabled)`:e.packageManagerClean?`${i.CLEAN} Package manager cache ${x(e,"cleaned")}`:`${i.WARN} Package manager cache not cleaned`,co=e=>e.rebuild===!1?`${i.BOX} Dependency install skipped (rebuild disabled)`:e.dependencyInstall?`${i.BOX} Dependencies ${x(e,"freshly installed")}`:`${i.FAIL} Failed to install dependencies`,lo=e=>{if(e.dockerClean==="NO_DOCKER")return[`${i.DOCKER} Docker steps skipped`];if(e.dockerClean==="DOCKER_UNAVAILABLE")return[`${i.FAIL} Docker is not available - Docker steps did not run`];if(e.dockerClean==="DOCKER_FAIL")return[`${i.FAIL} Failed to remove Docker containers`];let t=`${i.DOCKER} Docker containers${e.dockerVolumes?", images and volumes":" and images"} ${x(e,"removed")}`;return e.rebuild===!1?[t,`${i.DOCKER} Docker rebuild skipped (rebuild disabled)`]:[t,e.dockerRebuild?`${i.DOCKER} Docker containers ${x(e,"rebuilt from scratch")}`:`${i.FAIL} Failed to rebuild Docker containers`,e.dockerLaunch&&e.dockerRebuild?`${i.ROCKET} Services ${x(e,"running in detached mode")}`:`${i.FAIL} Failed to start Docker containers`]},uo=(e,t)=>t&&e.dryRun?`${i.FAIL} DRY RUN FOUND PROBLEMS - NOTHING WAS CHANGED`:t?`${i.FAIL} TORCHED WITH ERRORS - SEE OUTPUT ABOVE`:e.dryRun?"\u{1F525} DRY RUN COMPLETE - NOTHING WAS CHANGED \u{1F525}":"\u{1F525} PROJECT SUCCESSFULLY TORCHED! \u{1F525}",gt=e=>{let t=fe(e),o=ao(e);K([uo(e,t),"",...io(e),o,co(e),...lo(e),"",e.logfile===!1?`${i.CLIPBOARD} Logging to torch-it.log is disabled; set "logfile": true in torchrc.json to enable it`:`${i.CLIPBOARD} Check torch-it.log for detailed logs`],t?u.RED:u.GREEN)};var mt=f(require("readline"));function ht(e){let t=mt.createInterface({input:process.stdin,output:process.stdout});return new Promise(o=>{t.question(e,n=>{t.close();let s=n.trim().toLowerCase();o(s==="y"||s==="yes")})})}function ge(e){e.assumeYes!==!0&&process.env.TORCH_DRY_RUN!=="1"&&(g()&&(r("--json never prompts: add --yes or --test.","fail"),m()),process.stdin.isTTY!==!0&&(r("No interactive terminal to confirm on. Re-run with --yes to skip the prompt, or --test for a dry run.","fail"),process.env.MSYSTEM&&r("In Git Bash, 'winpty torch-it' gives it a terminal to ask on.","info"),m()))}async function po(e){j(()=>{r(`
|
|
100
|
+
Review the cleanup below. Matching paths and Docker actions (if enabled) will run next.`,"warn"),ue(e,{includeHelpFooter:!1}),r(`
|
|
101
|
+
These targets will be removed where they exist. Package caches may be cleared and dependencies reinstalled per your settings.`,"warn")}),await ht("Continue? Type Yes or No (y/n): ")||(r("Aborted. Nothing was changed.","info"),process.exit(p.CANCELLED))}function fo(e,t){if(!(t.dockerClean==="NO_DOCKER"||t.dockerClean==="DOCKER_UNAVAILABLE")){if(e.rebuild===!1){r("Rebuild disabled - skipping Docker rebuild","info");return}r(`${i.BUILD} DOCKER REBUILD`,"step"),t.dockerRebuild=ze(e),t.dockerRebuild&&(r(`${i.ROCKET} LAUNCH`,"step"),t.dockerLaunch=Ze(e))}}async function Ct(e,t={}){let o=process.env.TORCH_DRY_RUN==="1";ge(t),t.assumeYes!==!0&&!o&&await po(e);let n={dockerClean:"NO_DOCKER",buildAndCacheClean:!1,packageManagerClean:!1,dependencyInstall:!1,dockerRebuild:!1,dockerLaunch:!1,logfile:e.logfile,rebuild:e.rebuild,dryRun:o,cacheClean:e.cacheClean,dockerVolumes:e.dockerVolumes};r(`${i.CLEAN} DOCKER CLEANUP`,"step"),n.dockerClean=Qe(e),r(`${i.CLEAN} BUILD ARTIFACTS & CACHE CLEANUP`,"step");let s=dt(e);n.buildAndCacheClean=s.cleaned,n.cleanupFailures=s.failed,n.trackedKept=s.tracked,n.paths=s.paths;let c=J();return n.packageManager=c,e.cacheClean!==!1?(r("Cleaning package manager caches...","step"),n.packageManagerClean=ut(c)):r("Cache clean disabled - leaving the package manager cache alone","info"),e.rebuild!==!1?(Ae(),r(`${i.BUILD} DEPENDENCY INSTALLATION`,"step"),n.dependencyInstall=pt(c)):r("Rebuild disabled - skipping dependency installation","info"),fo(e,n),gt(n),n}var kt=f(require("fs")),bt=f(require("path"));function go(){return["package.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json"].some(t=>kt.existsSync(bt.join(process.cwd(),t)))}function Rt(){go()||(r("This directory does not contain a Node.js project.","fail"),r("Please create a Node.js project (with package.json) before using torch-it.","info"),m())}var mo=process.argv.slice(2),h=at(mo);ye(h.isJson&&!h.isHelp&&!h.isLlms);De({plain:h.isPlain,quiet:h.isQuiet});ct(h);lt(h);var me=H(h.filteredArgs);ge({assumeYes:h.assumeYes});P(me.logfile);me.logfile&&be();we();Rt();h.isDryRun&&r("Running in --test dry-run mode (no files or services will be changed)","info");(async()=>{let e=await Ct(me,{assumeYes:h.assumeYes}),t=fe(e)?p.STEP_FAILED:p.OK;g()&&O(Ee(e,t)),t!==p.OK&&(process.exitCode=t)})().catch(e=>{r(`Unexpected error: ${e}`,"fail"),m()});
|
package/llms.txt
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# torch-it
|
|
2
|
+
|
|
3
|
+
> A Node.js CLI that deletes a project's build output, caches and `node_modules`, clears the package manager cache, then reinstalls dependencies. Optionally tears down and rebuilds Docker Compose services. Describes torch-it 3.0.0 (`torch-it --version`). This file is the reference for AI agents and scripts; `README.md` is the human guide. `torch-it --llms` prints this file for the installed version, and `torch-it --help` prints a short version of the same rules.
|
|
4
|
+
|
|
5
|
+
## What it does and does not do
|
|
6
|
+
|
|
7
|
+
- Works on one directory: the current one, or the one given with `--cwd=<dir>`. That should be the project root. It does not descend into workspace packages: targets are paths relative to that directory.
|
|
8
|
+
- The directory must hold a `package.json`, `yarn.lock`, `pnpm-lock.yaml` or `npm-shrinkwrap.json`. Otherwise it exits `1` and changes nothing.
|
|
9
|
+
- Deletes with a recursive force remove. Nothing goes to a recycle bin. Files that git tracks are kept (see "Files tracked in git"); anything untracked or ignored that sits in a target is gone for good.
|
|
10
|
+
- `node_modules` is always a target, so a run with `--rebuild=false` leaves the project without dependencies until they are installed again.
|
|
11
|
+
- Clears the package manager's cache, and that cache is machine-wide, not per project (`npm cache clean --force`, `yarn cache clean` or `pnpm store prune`). Later installs in every project on the machine download again. Pass `--cacheClean=false` to leave it alone; a broken install is rarely the cache's fault.
|
|
12
|
+
- With Docker mode on, `docker compose down --rmi all` removes the project's containers and images. Volumes are kept unless `dockerVolumes` is also on, in which case data held in them, such as a development database, is lost.
|
|
13
|
+
- It never touches git, never edits source files and never changes `package.json` or lockfiles itself (the install step may update a lockfile, as any install can).
|
|
14
|
+
|
|
15
|
+
## The safe way to run it (agents and scripts)
|
|
16
|
+
|
|
17
|
+
1. Preview with `--test --json`: `removed` lists every path that would be removed, `keptTracked` what git protects, and `steps` what would run. Nothing is deleted and nothing is asked.
|
|
18
|
+
2. Show the user the list and get their agreement. Point out anything that is not obviously build output.
|
|
19
|
+
3. Run the same command with `--yes` in place of `--test`.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
torch-it --test --json # the plan as JSON, exit 0
|
|
23
|
+
torch-it --yes --json # the real run, result as JSON
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Without `--yes`, torch-it shows the plan and asks "Continue? Type Yes or No (y/n)" on a terminal. With no terminal (an agent's shell, CI, a pipe) it deletes nothing, writes nothing, prints an error and exits `1`. Do not pipe `y` into the prompt: use `--yes`. A Git Bash window can count as no terminal; there `winpty torch-it` gives it one.
|
|
27
|
+
|
|
28
|
+
To add or protect paths for one run, pass them to both commands:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
torch-it --test --protectedPaths=dist,uploads
|
|
32
|
+
torch-it --yes --protectedPaths=dist,uploads
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
A list given on the command line is ADDED to the list of the same name in `torchrc.json`. `--protectedPaths=dist` protects `dist` as well as everything the file protects. A flag can not take a path out of a list the file sets: edit the file for that.
|
|
36
|
+
|
|
37
|
+
## Options
|
|
38
|
+
|
|
39
|
+
| Option | Effect |
|
|
40
|
+
|---|---|
|
|
41
|
+
| `--test` | Dry run. Lists what would be removed and run, changes nothing, never prompts |
|
|
42
|
+
| `--yes`, `-y` | Run without the confirmation prompt |
|
|
43
|
+
| `--json` | Print one JSON document describing the run and nothing else on stdout. Needs `--yes` or `--test` |
|
|
44
|
+
| `--quiet`, `-q` | Print only warnings, errors and the final summary. Hides the progress output of the install and Docker commands |
|
|
45
|
+
| `--plain` | Plain output at a terminal. Already the case when output is captured |
|
|
46
|
+
| `--config` | Print the resolved settings and the targets that exist, then exit. Includes any overrides passed with it |
|
|
47
|
+
| `--help`, `-h` | Usage |
|
|
48
|
+
| `--llms` | Print this file, for the version that is installed |
|
|
49
|
+
| `--version`, `-v` | `torch-it v<version>` |
|
|
50
|
+
| `--protectedPaths=a,b` | Paths to keep |
|
|
51
|
+
| `--customPaths=a,b` | Extra paths to delete |
|
|
52
|
+
| `--only=a,b` | Remove only these paths. The default targets, the root file patterns and `customPaths` are skipped. Protection still applies |
|
|
53
|
+
| `--cwd=<dir>`, `--cwd <dir>` | Run in `<dir>` as if it were the current directory, including reading its `torchrc.json`. A directory that cannot be entered is exit `1` |
|
|
54
|
+
| `--rebuild=true\|false` | `false` skips the dependency install and the Docker rebuild. Cleanup still runs. Default `true` |
|
|
55
|
+
| `--dockerMode=true\|false` | Turn the Docker Compose steps on or off. Default `false` |
|
|
56
|
+
| `--dockerVolumes=true\|false` | In Docker mode, also remove volumes. Default `false` |
|
|
57
|
+
| `--cacheClean=true\|false` | `false` skips the package manager cache clean. Default `true` |
|
|
58
|
+
| `--allowTracked=true\|false` | `true` also removes files tracked in git. Default `false`: they are kept |
|
|
59
|
+
| `--logfile=true\|false` | Write the run's output to `torch-it.log` in the project root. Default `false` |
|
|
60
|
+
|
|
61
|
+
- A boolean option with no value means `true`: `--logfile` is `--logfile=true`.
|
|
62
|
+
- A list is comma-separated: `--customPaths=temp,logs`. A JSON array also works but must be quoted so the shell keeps the inner quotes: `'--customPaths=["temp","logs"]'`. A path that contains a comma needs the JSON form.
|
|
63
|
+
- Any other argument, and any value of the wrong type, is an error: torch-it prints each problem and exits `1` before deleting anything.
|
|
64
|
+
- `--help`, `--llms` and `--version` work in any directory and ignore every other argument. `--config` works in any directory but still rejects invalid options.
|
|
65
|
+
|
|
66
|
+
## torchrc.json
|
|
67
|
+
|
|
68
|
+
Optional file in the project root. For the boolean options the command line overrides it, and it overrides the defaults. For the list options the command line adds to it.
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"customPaths": ["apps/web/.next", "services/api/tmp"],
|
|
73
|
+
"protectedPaths": ["important-data", "dist/keep.json"],
|
|
74
|
+
"only": [],
|
|
75
|
+
"dockerMode": false,
|
|
76
|
+
"dockerVolumes": false,
|
|
77
|
+
"rebuild": true,
|
|
78
|
+
"cacheClean": true,
|
|
79
|
+
"allowTracked": false,
|
|
80
|
+
"logfile": false
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
- Keys: `customPaths`, `protectedPaths`, `only` (arrays of strings), `dockerMode`, `dockerVolumes`, `rebuild`, `cacheClean`, `allowTracked`, `logfile` (booleans). `customDirs` and `customFiles` are accepted and mean the same as `customPaths`.
|
|
85
|
+
- Invalid JSON, an unknown key or a value of the wrong type stops the run with exit `1`. Nothing is deleted.
|
|
86
|
+
- Paths are relative to the project root. `dist`, `dist/`, `./dist`, `dist\` and `apps/../dist` are the same path, and so is its absolute form.
|
|
87
|
+
- A `customPaths` or `only` entry that is the project root or lies outside it (`.`, `..`, `../other`) is refused: exit `1`, nothing deleted. torch-it only deletes inside the directory it is run in.
|
|
88
|
+
|
|
89
|
+
## What gets deleted
|
|
90
|
+
|
|
91
|
+
- **Default targets**: about 70 fixed paths, deleted where they exist. They include `node_modules`, `dist`, `build`, `out`, `coverage`, `tmp`, `temp`, `.next`, `.nuxt`, `.vite`, `.cache`, `.turbo`, `android/build` and `ios/Pods`. `README.md` has the full list, and `torch-it --config` shows the ones present in this project.
|
|
92
|
+
- **Root file patterns**: `*.log`, `*.tgz`, `*.tar.gz` and `tsconfig.tsbuildinfo`. The `*` patterns match files in the project root only, never directories and never files in subdirectories.
|
|
93
|
+
- **Custom paths**: whatever `customPaths` lists.
|
|
94
|
+
- **With `only` set**: just the paths it lists. The three groups above are skipped.
|
|
95
|
+
- **Not default targets**: `lib`, `es`, `cjs` and `umd`. They often hold source.
|
|
96
|
+
|
|
97
|
+
`build`, `out`, `tmp` and `temp` are default targets. In some projects those names hold source or data. Committed files in them are kept automatically; files that are neither committed nor build output (untracked work, ignored local data) are not, so check the `--test` output and protect them.
|
|
98
|
+
|
|
99
|
+
## How protection works
|
|
100
|
+
|
|
101
|
+
- A protected path is never removed, nor is anything inside it.
|
|
102
|
+
- A protected path inside a target is kept and the rest of the target is removed: with `protectedPaths: ["dist/keep.json"]`, `dist/keep.json` stays and the other contents of `dist` go.
|
|
103
|
+
- Matching is on whole path segments. Protecting `dist` does not protect `distribution`.
|
|
104
|
+
- Matching ignores case on every platform: protecting `Dist` protects `dist`.
|
|
105
|
+
- A protected path that does not exist changes nothing: the target around it is removed whole.
|
|
106
|
+
- If a target is a symbolic link and a protected path lies inside it, the link is left alone with a `Skipping` warning. Otherwise a link that is a target is removed as a link; what it points to is not touched.
|
|
107
|
+
- The preview from `--test`, `--config` and the confirmation prompt applies the same rules as the real run.
|
|
108
|
+
- Protection wins over `customPaths` when a path is in both.
|
|
109
|
+
- While `logfile` is on, `torch-it.log` is kept by the `*.log` sweep.
|
|
110
|
+
|
|
111
|
+
## Files tracked in git
|
|
112
|
+
|
|
113
|
+
- Before deleting, torch-it asks git which files inside the targets are tracked (`git ls-files`). Those files are treated as protected paths: they are kept and everything around them is removed.
|
|
114
|
+
- A `dist` holding one tracked `.gitkeep` is emptied and the `.gitkeep` stays. A `build` folder that is entirely committed is left untouched. A tracked `*.log` in the root is kept.
|
|
115
|
+
- This applies to every target: default targets, root file patterns, `customPaths` and `only`.
|
|
116
|
+
- When files were kept, a `warning: Kept N file(s) tracked in git, under: <folders>. Pass --allowTracked=true to remove them too.` line is printed, and the summary has `N file(s) tracked in git were kept`. The exit code is not affected.
|
|
117
|
+
- `--allowTracked=true` turns the guard off. Do not pass it unless the user has said the tracked files should go. Afterwards `git status` shows them as deleted and `git restore <path>` brings them back.
|
|
118
|
+
- Tracked means in the index: a file added with `git add` but not yet committed counts; an untracked or ignored file does not.
|
|
119
|
+
- Outside a git repository, or with no git on the PATH, there is no guard and nothing is reported.
|
|
120
|
+
|
|
121
|
+
## Order of work
|
|
122
|
+
|
|
123
|
+
1. Docker teardown, if Docker mode is on.
|
|
124
|
+
2. Default targets, then root file patterns, then custom paths. With `only` set: just the `only` paths.
|
|
125
|
+
3. Package manager cache clean, unless `cacheClean` is `false`.
|
|
126
|
+
4. Dependency install, unless `rebuild` is `false`.
|
|
127
|
+
5. Docker rebuild and start, if the teardown ran and `rebuild` is not `false`.
|
|
128
|
+
|
|
129
|
+
**Package manager**: chosen once per run, after the cleanup: the first of pnpm, yarn, npm whose lockfile (`pnpm-lock.yaml`, `yarn.lock`, `package-lock.json`) is present and whose command is on the PATH. With no lockfile: the first of npm, yarn, pnpm that is installed. It runs `<pm> install`, never `npm ci`.
|
|
130
|
+
|
|
131
|
+
**Docker mode** runs only when a Compose file is in the project root: `compose.yaml`, `compose.yml`, `docker-compose.yaml` or `docker-compose.yml`. A `Dockerfile` alone is not enough. It uses the `docker compose` plugin: `down --rmi all` (with `--volumes` added only when `dockerVolumes` is on), then `build --pull --no-cache`, then `up -d`. If Docker mode is on and Docker is not installed or the daemon is not running, the Docker steps do not run, the rest of the run continues, and the exit code is `2`. A dry run in Docker mode still runs the read-only `docker info` and `docker compose ps`.
|
|
132
|
+
|
|
133
|
+
## JSON output
|
|
134
|
+
|
|
135
|
+
`--json` is the form to use from an agent or a script. stdout carries exactly one JSON document; nothing else is written there.
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"version": "3.0.0",
|
|
140
|
+
"ok": true,
|
|
141
|
+
"exitCode": 0,
|
|
142
|
+
"dryRun": true,
|
|
143
|
+
"changed": false,
|
|
144
|
+
"cwd": "/home/me/my-app",
|
|
145
|
+
"packageManager": "npm",
|
|
146
|
+
"removed": ["node_modules", "dist/bundle.js", "debug.log"],
|
|
147
|
+
"failed": [],
|
|
148
|
+
"keptTracked": ["dist/.gitkeep"],
|
|
149
|
+
"steps": {
|
|
150
|
+
"dockerTeardown": "skipped",
|
|
151
|
+
"cleanup": "ok",
|
|
152
|
+
"cacheClean": "ok",
|
|
153
|
+
"install": "ok",
|
|
154
|
+
"dockerRebuild": "skipped",
|
|
155
|
+
"dockerStart": "skipped"
|
|
156
|
+
},
|
|
157
|
+
"warnings": ["Kept 1 file(s) tracked in git, under: dist. Pass --allowTracked=true to remove them too."],
|
|
158
|
+
"errors": []
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
- `ok` and `exitCode` match the process exit code. `changed` is `false` when nothing on disk can have changed: a dry run, or exit `1`.
|
|
163
|
+
- `removed`: paths removed. In a dry run, the paths that would be removed. Paths are relative to `cwd`.
|
|
164
|
+
- `failed`: paths that could not be removed. Not empty means exit `2`.
|
|
165
|
+
- `keptTracked`: files kept because git tracks them.
|
|
166
|
+
- `steps`: one of `ok`, `failed`, `skipped` per step; `dockerTeardown` can also be `unavailable`. In a dry run `ok` means the step would run.
|
|
167
|
+
- `warnings`, `errors`: the text of every warning and error line of the run.
|
|
168
|
+
- `packageManager`: `npm`, `yarn`, `pnpm` or `null` when none is installed.
|
|
169
|
+
- When nothing was changed because of exit `1`, the document has only `version`, `ok: false`, `exitCode: 1`, `changed: false`, `warnings` and `errors`.
|
|
170
|
+
- `--json` never prompts. Without `--yes` or `--test` it is exit `1`.
|
|
171
|
+
- stderr still gets the `warning:` and `error:` lines as they happen, and the output of `npm install` and `docker compose`. Read the JSON from stdout only.
|
|
172
|
+
- `torch-it --version --json` prints `{"version": "..."}`. `torch-it --config --json` prints `version`, `cwd` and the resolved `config`. `--help` and `--llms` ignore `--json`.
|
|
173
|
+
- New fields may be added in later versions. Do not fail on a field you do not know.
|
|
174
|
+
|
|
175
|
+
## Text output
|
|
176
|
+
|
|
177
|
+
Without `--json`, when stdout is not a terminal, which is always the case for an agent or a script, output is plain: no colour codes, emoji, banner or boxes. Each line has a fixed prefix:
|
|
178
|
+
|
|
179
|
+
| Prefix | Meaning | Stream |
|
|
180
|
+
|---|---|---|
|
|
181
|
+
| `> ` | A step is starting | stdout |
|
|
182
|
+
| `ok: ` | Something succeeded, e.g. `ok: dist removed` | stdout |
|
|
183
|
+
| `warning: ` | Something was skipped or needs a look | stderr |
|
|
184
|
+
| `error: ` | Something failed, e.g. `error: Failed to remove node_modules` | stderr |
|
|
185
|
+
| none | Information, and the summary lines at the end | stdout |
|
|
186
|
+
|
|
187
|
+
- Warnings and errors go to stderr, everything else to stdout. Capture both.
|
|
188
|
+
- A dry run prints one `Would remove <path>` line per path, with no prefix. A real run prints `> Removing <path>...` then `ok: <path> removed`.
|
|
189
|
+
- Use `--quiet` to keep the output short: only `warning:` and `error:` lines, the `Would remove` lines of a dry run and the summary are printed, and the install and Docker commands lose their progress output but keep their errors.
|
|
190
|
+
- The output of `npm install` and `docker compose` is passed straight through and is not prefixed.
|
|
191
|
+
- At a terminal the same messages carry colour and emoji instead of prefixes. `--plain` turns that off, and `NO_COLOR` turns off colour only.
|
|
192
|
+
|
|
193
|
+
## Exit codes
|
|
194
|
+
|
|
195
|
+
| Code | Meaning |
|
|
196
|
+
|---|---|
|
|
197
|
+
| `0` | Every step that was meant to run succeeded, a dry run found no problem, or `--config`, `--help`, `--llms` or `--version` was shown |
|
|
198
|
+
| `1` | Nothing was changed: invalid option or `torchrc.json`, not a Node.js project, a `--cwd` directory that cannot be entered, no terminal and no `--yes`, or `--json` with neither `--yes` nor `--test` |
|
|
199
|
+
| `2` | The run went ahead and at least one step failed: a path could not be removed, dependency install failed, Docker mode is on but Docker is not available, or a Docker teardown, rebuild or start failed |
|
|
200
|
+
| `3` | The user answered the prompt with anything but `y`/`yes`. Nothing was changed |
|
|
201
|
+
|
|
202
|
+
- A path that could not be deleted (locked or in use) prints `Failed to remove <path>`, the run carries on with the rest, and the exit code is `2`. On Windows the usual cause is a running dev server or editor holding `node_modules`: stop it and run again.
|
|
203
|
+
- A failed package manager cache clean is the one failure that does not change the exit code. The summary shows `Package manager cache not cleaned`.
|
|
204
|
+
- A dry run exits `2` when it can already see the real run would fail, for example Docker mode with no Docker. Nothing was changed.
|
|
205
|
+
- After exit `1` or `3` it is safe to fix the cause and run again. After exit `2` the project is partly cleaned: read the `error:` lines, fix the cause and run again, which is safe to repeat.
|
|
206
|
+
- The summary at the end of the text output starts with `PROJECT SUCCESSFULLY TORCHED`, `TORCHED WITH ERRORS`, `DRY RUN COMPLETE - NOTHING WAS CHANGED` or `DRY RUN FOUND PROBLEMS - NOTHING WAS CHANGED`.
|
|
207
|
+
- Prefer `--json` and its fields over matching text. Without it, rely on the exit code first, then on the line prefixes and the phrases quoted here. With `--logfile=true` the text output is also written to `torch-it.log`.
|
|
208
|
+
|
|
209
|
+
## Recipes
|
|
210
|
+
|
|
211
|
+
See what a run would do:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
torch-it --test
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Clean and reinstall without a prompt:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
torch-it --yes
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Delete and reinstall dependencies and touch nothing else. This is the smallest fix for a broken install and the one to try first:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
torch-it --yes --only=node_modules --cacheClean=false
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Clear some build output and nothing else:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
torch-it --yes --only=dist,.next --rebuild=false
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
A project that is not in the current directory:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
torch-it --cwd=apps/web --test
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Clean only, no reinstall and no Docker rebuild:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
torch-it --yes --rebuild=false
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Keep the build output, reset everything else:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
torch-it --yes --protectedPaths=dist,build
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Full reset including Docker Compose services:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
torch-it --test --dockerMode=true
|
|
257
|
+
torch-it --yes --dockerMode=true
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Without a global install: `npx torch-it --test`.
|
|
261
|
+
|
|
262
|
+
## Do not
|
|
263
|
+
|
|
264
|
+
- Do not run `torch-it --yes` before the user has seen the `--test` output for the same options.
|
|
265
|
+
- Do not turn on Docker mode unless the user asked for it, and never add `--dockerVolumes=true` on your own: volumes can hold data that exists nowhere else.
|
|
266
|
+
- Do not run a full torch when a narrower one would do. Start with `--only=node_modules --cacheClean=false`.
|
|
267
|
+
- Do not run it in a directory that is not the project root. From a subdirectory it works on that subdirectory. Use `--cwd` when the shell is somewhere else.
|
|
268
|
+
- Do not assume `build`, `out`, `tmp` or `temp` are disposable. Tracked files are kept for you, but untracked work in them is not.
|
|
269
|
+
- Do not pass `--allowTracked=true` on your own judgement.
|
|
270
|
+
- Do not leave the cache clean on when the user is on a slow or metered connection: it makes every later install download again.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "torch-it",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "CLI to reset, clean, and rebuild Node.js project environments.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cleanup",
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
"bugs": {
|
|
13
13
|
"url": "https://github.com/piyook/torch-it/issues"
|
|
14
14
|
},
|
|
15
|
-
"license": "
|
|
16
|
-
"author": "",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": "piyook",
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
19
19
|
"url": "git+https://github.com/piyook/torch-it.git"
|
|
@@ -23,48 +23,54 @@
|
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
25
|
"dist",
|
|
26
|
-
"README.md"
|
|
26
|
+
"README.md",
|
|
27
|
+
"llms.txt"
|
|
27
28
|
],
|
|
28
29
|
"type": "commonjs",
|
|
29
30
|
"main": "dist/torch-it.js",
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=20"
|
|
33
|
+
},
|
|
30
34
|
"scripts": {
|
|
31
|
-
"build": "esbuild src/torch.ts --bundle --outfile=dist/torch-it.js --platform=node --format=cjs --minify --banner:js=\"#!/usr/bin/env node\"",
|
|
35
|
+
"build": "esbuild src/torch.ts --bundle --outfile=dist/torch-it.js --platform=node --format=cjs --target=node20 --minify --banner:js=\"#!/usr/bin/env node\"",
|
|
32
36
|
"torch-it": "npm run build && node dist/torch-it.js",
|
|
33
|
-
"torch-it:ts": "
|
|
37
|
+
"torch-it:ts": "tsx src/torch.ts",
|
|
34
38
|
"lint": "oxlint .",
|
|
35
39
|
"lint:fix": "oxlint . --fix",
|
|
36
|
-
"format": "prettier --check src/**/*.{ts,js,json} tests/**/*.{ts,js,json} package.json package-lock.json .commitlintrc.json",
|
|
37
|
-
"format:write": "prettier --write src/**/*.{ts,js,json} tests/**/*.{ts,js,json} package.json package-lock.json .commitlintrc.json",
|
|
38
|
-
"
|
|
40
|
+
"format": "prettier --check \"src/**/*.{ts,js,json}\" \"tests/**/*.{ts,js,json}\" package.json package-lock.json .commitlintrc.json",
|
|
41
|
+
"format:write": "prettier --write \"src/**/*.{ts,js,json}\" \"tests/**/*.{ts,js,json}\" package.json package-lock.json .commitlintrc.json",
|
|
42
|
+
"fallow": "fallow",
|
|
43
|
+
"quality": "npm run lint && npm run format && npm run fallow && npm test",
|
|
39
44
|
"validate-branch-name": "validate-branch-name",
|
|
40
45
|
"prepare": "husky",
|
|
41
46
|
"prepublishOnly": "npm run build",
|
|
42
47
|
"test": "tsc --noEmit && vitest run",
|
|
43
48
|
"test:watch": "vitest"
|
|
44
49
|
},
|
|
45
|
-
"dependencies": {},
|
|
46
50
|
"devDependencies": {
|
|
47
|
-
"@commitlint/cli": "^
|
|
48
|
-
"@commitlint/config-conventional": "^
|
|
49
|
-
"@types/node": "^
|
|
50
|
-
"esbuild": "^0.28.
|
|
51
|
+
"@commitlint/cli": "^21.2.3",
|
|
52
|
+
"@commitlint/config-conventional": "^21.2.3",
|
|
53
|
+
"@types/node": "^26.6.4",
|
|
54
|
+
"esbuild": "^0.28.2",
|
|
55
|
+
"fallow": "^3.33.0",
|
|
51
56
|
"husky": "^9.1.7",
|
|
52
|
-
"lint-staged": "^
|
|
53
|
-
"oxlint": "^1.
|
|
54
|
-
"prettier": "^3.
|
|
55
|
-
"
|
|
56
|
-
"typescript": "^
|
|
57
|
+
"lint-staged": "^17.6.0",
|
|
58
|
+
"oxlint": "^1.87.0",
|
|
59
|
+
"prettier": "^3.9.9",
|
|
60
|
+
"tsx": "^4.23.15",
|
|
61
|
+
"typescript": "^7.0.2",
|
|
57
62
|
"validate-branch-name": "^1.3.2",
|
|
58
|
-
"vitest": "^
|
|
63
|
+
"vitest": "^5.0.3"
|
|
59
64
|
},
|
|
60
65
|
"lint-staged": {
|
|
61
|
-
"*.{ts,tsx,js,jsx,mjs,cjs
|
|
66
|
+
"*.{ts,tsx,js,jsx,mjs,cjs}": [
|
|
62
67
|
"oxlint --fix",
|
|
63
68
|
"prettier --write"
|
|
64
|
-
]
|
|
69
|
+
],
|
|
70
|
+
"*.json": "prettier --write"
|
|
65
71
|
},
|
|
66
72
|
"validate-branch-name": {
|
|
67
73
|
"pattern": "^(main|dev){1}$|^(feat|fix|hotfix|release|chore)/.+$",
|
|
68
|
-
"errorMsg": "INVALID BRANCH NAME: use format 'feat|fix|hotfix|release|
|
|
74
|
+
"errorMsg": "INVALID BRANCH NAME: use format 'feat|fix|hotfix|release|chore/your-branch-name'"
|
|
69
75
|
}
|
|
70
76
|
}
|