Skip to content

Commit

Permalink
Add trash support to CLI instead of fully deleting
Browse files Browse the repository at this point in the history
The 'trash' NPM package is added
This dependency uses ESModule, requiring project refactoring
Fixes voidcosmos#60
  • Loading branch information
Fleker committed Oct 16, 2021
1 parent d487c12 commit 4675644
Show file tree
Hide file tree
Showing 31 changed files with 8,351 additions and 98 deletions.
1 change: 1 addition & 0 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ To exit, <kbd>Q</kbd> or <kbd>Ctrl</kbd> + <kbd>c</kbd> if you're brave.
| -gb | Show folders in Gigabytes instead of Megabytes. |
| -h, --help, ? | Show this help page and exit |
| -nu, --no-check-update | Don't check for updates on startup |
| -tr, --trash | Move files to system trash dir instead of deleting directly. |
| -s, --sort | Sort results by: size or path _[ beta ]_ |
| -t, --target | Specify the name of the directories you want to search (by default, is node_modules) |
| -v, --version | Show npkill version |
Expand Down
14 changes: 10 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
"name": "npkill",
"version": "0.8.2",
"description": "List any node_modules directories in your system, as well as the space they take up. You can then select which ones you want to erase to free up space.",
"main": "lib/index.js",
"exports": "./lib/index.js",
"type": "module",
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"publishConfig": {
"access": "public"
},
Expand Down Expand Up @@ -35,11 +39,12 @@
"build": "gulp",
"build-go-bin": "gulp buildGo",
"start": "ts-node -r tsconfig-paths/register ./src/main.ts",
"start2": "node --loader ts-node/esm ./src/main.ts",
"test": "jest --verbose",
"test:watch": "jest --watch",
"test:mutant": "stryker run",
"release": "npm run build && np",
"debug": "TS_NODE_FILES=true node --inspect -r ts-node/register -r tsconfig-paths/register ./src/main.ts"
"debug": "TS_NODE_FILES=true node --inspect -r ts-node/register --loader ts-node/esm ./src/main.ts"
},
"dependencies": {
"ansi-escapes": "^4.3.1",
Expand All @@ -48,6 +53,7 @@
"keypress": "^0.2.1",
"node-emoji": "^1.10.0",
"rxjs": "^6.5.4",
"trash": "^8.0.0",
"tsconfig-paths": "^3.9.0"
},
"devDependencies": {
Expand All @@ -71,7 +77,7 @@
"remark-lint": "^7.0.0",
"rimraf": "^3.0.2",
"ts-jest": "^25.3.0",
"ts-node": "^8.8.1",
"ts-node": "^10.3.0",
"tslint": "^6.1.0",
"typescript": "^3.8.3"
},
Expand Down Expand Up @@ -106,4 +112,4 @@
"git add"
]
}
}
}
5 changes: 5 additions & 0 deletions src/constants/cli.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ export const OPTIONS: ICliOptions[] = [
description: 'Dont check for updates on startup.',
name: 'no-check-updates',
},
{
arg: ['-tr', '--trash'],
description: 'Move files to system trash dir instead of deleting directly.',
name: 'trash',
},
{
arg: ['-s', '--sort'],
description: 'Sort results by: size or path',
Expand Down
14 changes: 7 additions & 7 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export * from './cli.constants';
export * from './main.constants';
export * from './messages.constants';
export * from './sort.result';
export * from './spinner.constants';
export * from './update.constants';
export * from './recursive-rmdir-node-support.constants';
export * from './cli.constants.js';
export * from './main.constants.js';
export * from './messages.constants.js';
export * from './sort.result.js';
export * from './spinner.constants.js';
export * from './update.constants.js';
export * from './recursive-rmdir-node-support.constants.js';
1 change: 1 addition & 0 deletions src/constants/main.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const DEFAULT_CONFIG: IConfig = {
showErrors: false,
sortBy: '',
targetFolder: 'node_modules',
trash: false,
};

export const MARGINS = {
Expand Down
43 changes: 28 additions & 15 deletions src/controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as colors from 'colors';
import * as keypress from 'keypress';
import colors from 'colors';
import keypress from 'keypress';

import {
BANNER,
Expand All @@ -12,42 +12,43 @@ import {
UI_HELP,
UI_POSITIONS,
VALID_KEYS,
} from '@core/constants/main.constants';
import { COLORS, HELP_WARNING, OPTIONS } from '@core/constants/cli.constants';
} from './constants/main.constants.js';
import { COLORS, HELP_WARNING, OPTIONS } from './constants/cli.constants.js';
import {
ConsoleService,
FileService,
ResultsService,
SpinnerService,
UpdateService,
} from '@core/services';
} from './services/index.js';
import {
ERROR_MSG,
HELP_MSGS,
INFO_MSGS,
} from '@core/constants/messages.constants';
} from './constants/messages.constants.js';
import {
IConfig,
IFolder,
IKeyPress,
IKeysCommand,
IListDirParams,
IPosition,
} from '@core/interfaces';
} from './interfaces/index.js';
import { Observable, Subject, from, interval } from 'rxjs';
import { SPINNERS, SPINNER_INTERVAL } from '@core/constants/spinner.constants';
import { SPINNERS, SPINNER_INTERVAL } from './constants/spinner.constants.js';
import {
catchError,
filter,
map,
mergeMap,
takeUntil,
tap,
} from 'rxjs/operators';
} from 'rxjs/operators/index.js';

import { FOLDER_SORT } from './constants/sort.result';
import { FOLDER_SORT } from './constants/sort.result.js';
import ansiEscapes from 'ansi-escapes';
import { bufferUntil } from './libs/buffer-until';
import { bufferUntil } from './libs/buffer-until.js';
import __dirname from './dirname.js';

export class Controller {
private folderRoot = '';
Expand Down Expand Up @@ -118,6 +119,9 @@ export class Controller {
}
this.config.sortBy = options['sort-by'];
}
if (options['trash']) {
this.config.trash = true
}

const exclude = options['exclude'];

Expand Down Expand Up @@ -658,10 +662,15 @@ export class Controller {
this.cursorPosY - MARGINS.ROW_RESULTS_START
];
this.clearErrors();
this.deleteFolder(nodeFolder);
this.deleteFolder(nodeFolder, this.config.trash);
}

private deleteFolder(folder: IFolder): void {
/**
* Deletes the user-selected folder.
* @param folder Folder to be deleted including deletion state
* @param moveToTrash Whether to move to system trash (true) or directly delete permanently (false).
*/
private deleteFolder(folder: IFolder, moveToTrash: boolean): void {
const isSafeToDelete = this.fileService.isSafeToDelete(
folder.path,
this.config.targetFolder,
Expand All @@ -675,9 +684,13 @@ export class Controller {
this.printFoldersSection();

this.fileService
.deleteDir(folder.path)
.deleteDir(folder.path, moveToTrash)
.then(() => {
folder.status = 'deleted';
if (moveToTrash) {
folder.status = 'trashed';
} else {
folder.status = 'deleted';
}
this.printStats();
this.printFoldersSection();
})
Expand Down
11 changes: 11 additions & 0 deletions src/dirname.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @fileoverview A helper module to get `__dirname` in an ESModule env
* @see https://stackoverflow.com/questions/64383909/dirname-is-not-defined-in-node-14-version
*/
import { fileURLToPath } from 'url';
import { dirname } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

export default __dirname
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

const tsConfig = require('./tsconfig.build.json');
const tsConfigPaths = require('tsconfig-paths');
import __dirname from './dirname.js';

const baseUrl = __dirname;
const cleanup = tsConfigPaths.register({
Expand Down
1 change: 1 addition & 0 deletions src/interfaces/config.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ export interface IConfig {
showErrors: boolean;
sortBy: string;
targetFolder: string;
trash: boolean;
exclude: string[];
}
2 changes: 1 addition & 1 deletion src/interfaces/file-service.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Observable } from 'rxjs';
export interface IFileService {
getFolderSize(path: string): Observable<any>;
listDir(params: IListDirParams): Observable<Buffer>;
deleteDir(path: string): Promise<{}>;
deleteDir(path: string, moveToTrash: boolean): Promise<{}>;
convertKbToGB(kb: number): number;
convertBytesToKB(bytes: number): number;
convertGBToMB(gb: number): number;
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/folder.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export interface IFolder {
path: string;
size: number;
isDangerous: boolean;
status: 'live' | 'deleting' | 'error-deleting' | 'deleted';
status: 'live' | 'deleting' | 'error-deleting' | 'deleted' | 'trashed';
}

/* interface IFolderStatus {
Expand Down
8 changes: 4 additions & 4 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import {
StreamService,
UpdateService,
WindowsFilesService,
} from '@core/services';
} from './services/index.js';

import { Controller } from './controller';
import { IFileService } from '@core/interfaces/file-service.interface';
import { MacFilesService } from './services/mac-files.service';
import { Controller } from './controller.js';
import { IFileService } from './interfaces/file-service.interface.js';
import { MacFilesService } from './services/mac-files.service.js';

const getOS = () => process.platform;

Expand Down
4 changes: 2 additions & 2 deletions src/services/console.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { OPTIONS, WIDTH_OVERFLOW } from '@core/constants';
import { OPTIONS, WIDTH_OVERFLOW } from '../constants/index.js';

import { ICliOptions } from '@core/interfaces/cli-options.interface';
import { ICliOptions } from '../interfaces/cli-options.interface.js';

export class ConsoleService {
getParameters(rawArgv: string[]): {} {
Expand Down
2 changes: 1 addition & 1 deletion src/services/files.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Observable } from 'rxjs';
export abstract class FileService implements IFileService {
abstract getFolderSize(path: string): Observable<any>;
abstract listDir(params: IListDirParams): Observable<Buffer>;
abstract deleteDir(path: string): Promise<{}>;
abstract deleteDir(path: string, moveToTrash: boolean): Promise<{}>;

convertKbToGB(kb: number): number {
const factorKBtoGB = 1048576;
Expand Down
18 changes: 9 additions & 9 deletions src/services/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
export * from './console.service';
export * from './files.service';
export * from './https.service';
export * from './linux-files.service';
export * from './results.service';
export * from './spinner.service';
export * from './stream.service';
export * from './update.service';
export * from './windows-files.service';
export * from './console.service.js';
export * from './files.service.js';
export * from './https.service.js';
export * from './linux-files.service.js';
export * from './results.service.js';
export * from './spinner.service.js';
export * from './stream.service.js';
export * from './update.service.js';
export * from './windows-files.service.js';
4 changes: 2 additions & 2 deletions src/services/linux-files.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { spawn } from 'child_process';

import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { UnixFilesService } from './unix-files.service';
import { map } from 'rxjs/operators/index.js';
import { UnixFilesService } from './unix-files.service.js';

export class LinuxFilesService extends UnixFilesService {
getFolderSize(path: string): Observable<number> {
Expand Down
4 changes: 2 additions & 2 deletions src/services/mac-files.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Observable } from 'rxjs';
import { spawn } from 'child_process';
import { map } from 'rxjs/operators';
import { UnixFilesService } from './unix-files.service';
import { map } from 'rxjs/operators/index.js';
import { UnixFilesService } from './unix-files.service.js';

export class MacFilesService extends UnixFilesService {
getFolderSize(path: string): Observable<number> {
Expand Down
4 changes: 2 additions & 2 deletions src/services/results.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IFolder, IStats } from '@interfaces/index';
import { FOLDER_SORT } from '@core/constants/sort.result';
import { IFolder, IStats } from '../interfaces/index.js';
import { FOLDER_SORT } from '../constants/sort.result.js';

export class ResultsService {
results: IFolder[] = [];
Expand Down
2 changes: 1 addition & 1 deletion src/services/stream.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ChildProcessWithoutNullStreams } from 'child_process';
import { Observable } from 'rxjs';
import { STREAM_ENCODING } from '@core/constants';
import { STREAM_ENCODING } from '../constants/index.js';

export class StreamService {
streamToObservable<T>(stream: ChildProcessWithoutNullStreams) {
Expand Down
28 changes: 17 additions & 11 deletions src/services/unix-files.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { StreamService } from './stream.service';
import { StreamService } from './stream.service.js';
import { Observable } from 'rxjs';
import { spawn, exec } from 'child_process';
import { IListDirParams } from '@core/interfaces';
import { FileService } from './files.service';
import { IListDirParams } from '../interfaces/index.js';
import { FileService } from './files.service.js';
import trash from 'trash';

export abstract class UnixFilesService extends FileService {
constructor(protected streamService: StreamService) {
Expand All @@ -19,15 +20,20 @@ export abstract class UnixFilesService extends FileService {
return this.streamService.getStream(child);
}

deleteDir(path: string): Promise<{}> {
return new Promise((resolve, reject) => {
const command = `rm -rf "${path}"`;
exec(command, (error, stdout, stderr) => {
if (error) return reject(error);
if (stderr) return reject(stderr);
resolve(stdout);
deleteDir(path: string, moveToTrash: boolean): Promise<{}> {
if (moveToTrash) {
return trash(path)
.then(() => Promise.resolve({}))
} else {
return new Promise((resolve, reject) => {
const command = `rm -rf "${path}"`;
exec(command, (error, stdout, stderr) => {
if (error) return reject(error);
if (stderr) return reject(stderr);
resolve(stdout);
});
});
});
}
}

protected prepareFindArgs(params: IListDirParams): string[] {
Expand Down
6 changes: 3 additions & 3 deletions src/services/update.service.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {
VERSION_CHECK_DIRECTION,
VERSION_KEY,
} from '@core/constants/update.constants';
} from '../constants/update.constants.js';

import { HttpsService } from './https.service';
import { HttpsService } from './https.service.js';

export class UpdateService {
constructor(private httpsService: HttpsService) {}
constructor(private httpsService: HttpsService) { }

async isUpdated(localVersion: string): Promise<boolean> {
const remoteVersion = await this.getRemoteVersion();
Expand Down
Loading

0 comments on commit 4675644

Please sign in to comment.