1- #!/usr/bin/env -S pkgx --quiet deno^2.1 run --ext=ts --allow-sys=uid --allow-run --allow-env=PKGX_DIR,HOMEBREW_PREFIX,HOME,PATH --allow-read --allow-write
2- import { Path , SemVer , semver } from "https://deno.land/x/libpkgx@v0.20.3/mod.ts" ;
1+ #!/usr/bin/env -S pkgx --quiet deno^2.1 run --ext=ts --allow-sys=uid --allow-run --allow-env --allow-read --allow-write --allow-ffi
2+ import {
3+ Path ,
4+ SemVer ,
5+ semver ,
6+ } from "https://deno.land/x/libpkgx@v0.20.3/mod.ts" ;
37import { dirname , fromFileUrl , join } from "jsr:@std/path@^1" ;
48import { ensureDir , existsSync , walk } from "jsr:@std/fs@^1" ;
59import { parseArgs } from "jsr:@std/cli@^1" ;
@@ -66,6 +70,10 @@ if (parsedArgs.help) {
6670 break ;
6771 case "uninstall" :
6872 case "rm" :
73+ for ( const arg of args ) {
74+ await uninstall ( arg ) ;
75+ }
76+ break ;
6977 case "list" :
7078 case "ls" :
7179 for await ( const path of ls ( ) ) {
@@ -424,10 +432,13 @@ function get_pkgx() {
424432}
425433
426434async function * ls ( ) {
427- for ( const path of [ Path . root . join ( "/usr/local/pkgs" ) , Path . home ( ) . join ( ".local/pkgs" ) ] ) {
435+ for (
436+ const path of [ new Path ( "/usr/local/pkgs" ) , Path . home ( ) . join ( ".local/pkgs" ) ]
437+ ) {
438+ if ( ! path . isDirectory ( ) ) continue ;
428439 const dirs = [ path ] ;
429440 let dir : Path | undefined ;
430- while ( dir = dirs . pop ( ) ) {
441+ while ( ( dir = dirs . pop ( ) ) != undefined ) {
431442 for await ( const [ path , { name, isDirectory, isSymlink } ] of dir . ls ( ) ) {
432443 if ( ! isDirectory || isSymlink ) continue ;
433444 if ( / ^ v \d + \. / . test ( name ) ) {
@@ -439,3 +450,99 @@ async function* ls() {
439450 }
440451 }
441452}
453+
454+ import { hooks , plumbing } from "https://deno.land/x/libpkgx@v0.20.3/mod.ts" ;
455+
456+ async function uninstall ( arg : string ) {
457+ let found : { project : string } | undefined =
458+ ( await hooks . usePantry ( ) . find ( arg ) ) ?. [ 0 ] ;
459+ if ( ! found ) {
460+ found = await plumbing . which ( arg ) ;
461+ }
462+ if ( ! found ) throw new Error ( `pkg not found: ${ arg } ` ) ;
463+
464+ const set = new Set < string > ( ) ;
465+ const files : Path [ ] = [ ] ;
466+ let dirs : Path [ ] = [ ] ;
467+ const pkg_dirs : Path [ ] = [ ] ;
468+ for ( const root of [ new Path ( "/usr/local" ) , Path . home ( ) . join ( ".local" ) ] ) {
469+ const dir = root . join ( "pkgs" , found . project ) . isDirectory ( ) ;
470+ if ( ! dir ) continue ;
471+ pkg_dirs . push ( dir ) ;
472+ for await ( const [ pkgdir , { isDirectory } ] of dir . ls ( ) ) {
473+ if ( ! isDirectory ) continue ;
474+ for await ( const { path, isDirectory } of walk ( pkgdir . string ) ) {
475+ const leaf = new Path ( path ) . relative ( { to : pkgdir } ) ;
476+ const resolved_path = root . join ( leaf ) ;
477+ if ( set . has ( resolved_path . string ) ) continue ;
478+ if ( ! resolved_path . exists ( ) ) continue ;
479+ if ( isDirectory ) {
480+ dirs . push ( resolved_path ) ;
481+ } else {
482+ files . push ( resolved_path ) ;
483+ }
484+ }
485+ }
486+ }
487+
488+ // we need to delete this in a heirachical fashion or they don’t delete
489+ dirs = dirs . sort ( ) . reverse ( ) ;
490+
491+ if ( files . length == 0 ) {
492+ console . error ( "not installed" ) ;
493+ Deno . exit ( 1 ) ;
494+ }
495+
496+ const needs_sudo = files . some ( ( p ) => p . string . startsWith ( "/usr/local" ) ) ;
497+ if ( needs_sudo ) {
498+ {
499+ const { success, code } = await new Deno . Command ( "/usr/bin/sudo" , {
500+ args : [ "rm" , ...files . map ( ( p ) => p . string ) ] ,
501+ } ) . spawn ( ) . status ;
502+ if ( ! success ) Deno . exit ( code ) ;
503+ }
504+ {
505+ await new Deno . Command ( "/usr/bin/sudo" , {
506+ args : [ "rmdir" , ...dirs . map ( ( p ) => p . string ) ] ,
507+ stderr : "null" ,
508+ } ) . spawn ( ) . status ;
509+ }
510+
511+ const { success, code } = await new Deno . Command ( "/usr/bin/sudo" , {
512+ args : [
513+ "rm" ,
514+ "-rf" ,
515+ ...pkg_dirs . map ( ( p ) => p . string ) ,
516+ ...pkg_dirs . map ( ( x ) => x . parent ( ) . string ) ,
517+ ] ,
518+ } ) . spawn ( ) . status ;
519+ if ( ! success ) Deno . exit ( code ) ;
520+
521+ await new Deno . Command ( "/usr/bin/sudo" , {
522+ args : [
523+ "rmdir" ,
524+ "/usr/local/pkgs" ,
525+ Path . home ( ) . join ( ".local/pkgs" ) . string ,
526+ ] ,
527+ stderr : "null" ,
528+ } ) . spawn ( ) . status ;
529+ } else {
530+ for ( const path of files ) {
531+ if ( ! path . isDirectory ( ) ) {
532+ Deno . removeSync ( path . string ) ;
533+ }
534+ }
535+ for ( const path of dirs ) {
536+ if ( path . isDirectory ( ) ) {
537+ try {
538+ Deno . removeSync ( path . string ) ;
539+ } catch {
540+ // some dirs will not be removable
541+ }
542+ }
543+ }
544+ for ( const path of pkg_dirs ) {
545+ Deno . removeSync ( path . string , { recursive : true } ) ;
546+ }
547+ }
548+ }
0 commit comments