diff --git a/CHANGES.md b/CHANGES.md index 8964d5f04..f867258b2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,14 @@ Version 2.0.25 To be released. +### @fedify/init + + - Added permanent removal warning for Linux or other UNIX-like system users + before delete the existing content in the project directory. + [[#989] by Jungmin Yoon\] + +[#989]: https://github.com/fedify-dev/fedify/issues/989 + Version 2.0.24 -------------- diff --git a/changes.d/init/permanent-delete.md b/changes.d/init/permanent-delete.md new file mode 100644 index 000000000..93c66b6d0 --- /dev/null +++ b/changes.d/init/permanent-delete.md @@ -0,0 +1,5 @@ + - Added permanent removal warning for Linux or other UNIX-like system users + before delete the existing content in the project directory. + [[#989] by Jungmin Yoon] + +[#989]: https://github.com/fedify-dev/fedify/issues/989 diff --git a/packages/init/src/ask/dir.ts b/packages/init/src/ask/dir.ts index 513de170c..0ed270c30 100644 --- a/packages/init/src/ask/dir.ts +++ b/packages/init/src/ask/dir.ts @@ -1,10 +1,10 @@ import { identity, pipe, when } from "@fxts/core"; import { input } from "@inquirer/prompts"; -import { message } from "@optique/core/message"; +import { type Message, message } from "@optique/core/message"; import { printError } from "@optique/run"; import toggle from "inquirer-toggle"; -import { getCwd, getOsType, runSubCommand } from "../utils.ts"; import { isDirectoryEmpty, logger } from "../lib.ts"; +import { getCwd, getOsType, runSubCommand } from "../utils.ts"; /** * Fills in the project directory by prompting the user if not provided. @@ -26,12 +26,14 @@ const fillDir: ( export default fillDir; +type DirActionMode = "trash" | "permanent"; + const askDir = (cwd: string) => input({ message: "Project directory:", default: cwd }); const askIfNonEmpty = async (dir: string) => { if (await isDirectoryEmpty(dir)) return true; - if (await askNonEmpty(dir)) return await moveDirToTrash(dir); + if (await askNonEmpty(dir)) return await moveOrDeleteDir(dir); return false; }; @@ -42,47 +44,91 @@ Do you want to use it anyway?`, default: false, }); -const moveDirToTrash = (dir: string) => - pipe(dir, askMoveToTrash, when(identity, moveToTrash(dir))); +const moveOrDeleteDir = (dir: string) => + pipe( + dir, + buildMoveOrDelete, + (action) => + pipe( + action.confirm, + when(identity, runAction(action.command, action.error)), + ), + ); + +const buildMoveOrDelete = (dir: string) => + pipe( + getOsType(), + detectSupportedAction, + (fn) => { + return { + confirm: fn.confirmAction(dir), + command: fn.actionPlan.command(dir), + error: errorMessages[fn.actionPlan.mode](dir), + }; + }, + ); + +const detectSupportedAction = (os: NodeJS.Platform) => { + const actionPlan = + trashOrDeleteCommands[os as keyof typeof trashOrDeleteCommands] ?? + trashOrDeleteCommands.linux; + + return { + confirmAction: (actionPlan.mode === "trash") + ? moveToTrashConfirm + : deletePermanentlyConfirm, + actionPlan, + }; +}; -const askMoveToTrash = (dir: string) => +const moveToTrashConfirm = (dir: string) => toggle.default({ message: `Do you want to move the contents of "${dir}" to the trash? If you choose "No", you should choose another directory.`, default: false, }); -const moveToTrash = (dir: string) => () => +const deletePermanentlyConfirm = (dir: string) => + toggle.default({ + message: `Do you really want to delete all contents of "${dir}" PERMANENTLY? +If you choose "No", you should choose another directory.`, + default: false, + }); + +const runAction = (command: string[], error: Message) => () => pipe( - getOsType(), - getTrashCommand, - (fn) => fn(dir), + command, (cmd) => runSubCommand(cmd, { stdio: "ignore" }), () => true, ).catch((e) => { logger.error(e); - printError(message`Failed to move ${dir} to trash. -Please move it manually.`); + printError(error); return false; }); -const getTrashCommand = (os: NodeJS.Platform) => - trashCommands[os as keyof typeof trashCommands] ?? trashCommands.linux; - -const trashCommands: Record< +const trashOrDeleteCommands: Record< Extract, - (dir: string) => string[] + { mode: DirActionMode; command: (dir: string) => string[] } > = { // mac - darwin: (dir: string) => ["trash", dir], + darwin: { + mode: "trash", + command: (dir: string) => ["trash", dir], + }, // windows - win32: (dir: string) => [ - "powershell", - "-Command", - getPowershellTrashCommand(dir), - ], + win32: { + mode: "trash", + command: (dir: string) => [ + "powershell", + "-Command", + getPowershellTrashCommand(dir), + ], + }, // other unix - linux: (dir: string) => ["rm", "-rf", dir], + linux: { + mode: "permanent", + command: (dir: string) => ["rm", "-rf", dir], + }, }; const getPowershellTrashCommand = (dir: string) => @@ -96,3 +142,12 @@ const getPowershellTrashCommand = (dir: string) => "'OnlyErrorDialogs',", "'SendToRecycleBin')", ].join(" "); + +const errorMessages: Record Message> = { + "trash": (dir: string) => + message`Failed to move ${dir} to trash. +Please move it manually.`, + "permanent": (dir: string) => + message`Failed to delete ${dir} permanently. +Please remove it manually.`, +};