( plan: BinaryRemovalPlan, deps: RemoveBinaryDeps = defaultRemoveDeps(), )
| 261 | } |
| 262 | |
| 263 | export function executeBinaryRemoval( |
| 264 | plan: BinaryRemovalPlan, |
| 265 | deps: RemoveBinaryDeps = defaultRemoveDeps(), |
| 266 | ): BinaryRemovalResult { |
| 267 | const result: BinaryRemovalResult = { removed: [], leftovers: [], npm: 'skipped' }; |
| 268 | |
| 269 | const P = pathFor(deps.platform); |
| 270 | for (const p of plan.paths) { |
| 271 | // Planner-emitted paths are always deep, specific artifacts; this guard |
| 272 | // exists so no future planner bug can ever hand the executor a root. |
| 273 | if (P.resolve(p) === P.parse(P.resolve(p)).root) { |
| 274 | result.leftovers.push(p); |
| 275 | continue; |
| 276 | } |
| 277 | try { |
| 278 | deps.rm(p); |
| 279 | result.removed.push(p); |
| 280 | } catch { |
| 281 | // Windows: the running exe inside this tree is deletable-after-rename. |
| 282 | const moved = moveLockedExeAside(p, deps); |
| 283 | try { |
| 284 | deps.rm(p); |
| 285 | result.removed.push(p); |
| 286 | if (moved) result.leftovers.push(moved); |
| 287 | } catch { |
| 288 | result.leftovers.push(p); |
| 289 | } |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | if (plan.npmGlobal) { |
| 294 | // If we are RUNNING from the npm install on Windows, npm's delete will |
| 295 | // hit the same lock — move the exe aside first. |
| 296 | let moved: string | null = null; |
| 297 | if (plan.npmRoot) moved = moveLockedExeAside(plan.npmRoot, deps); |
| 298 | const inv = npmInvocation(deps.platform, ['uninstall', '-g', NPM_PACKAGE]); |
| 299 | const code = deps.run(inv.cmd, inv.args); |
| 300 | result.npm = code === 0 ? 'removed' : 'failed'; |
| 301 | if (moved) result.leftovers.push(moved); |
| 302 | } |
| 303 | |
| 304 | return result; |
| 305 | } |
no test coverage detected