2025-02-25 13:59:26 +01:00
|
|
|
import path from "path";
|
|
|
|
|
import inquirer from "inquirer";
|
|
|
|
|
import boxen from "boxen";
|
|
|
|
|
import chalk from "chalk";
|
|
|
|
|
import fs from "fs";
|
|
|
|
|
import { filesystemSync } from "fs-filesystem";
|
2025-02-17 15:13:47 +01:00
|
|
|
|
|
|
|
|
function showMsg(msg) {
|
2025-02-25 13:59:26 +01:00
|
|
|
console.log(
|
|
|
|
|
boxen(chalk.white(msg), {
|
|
|
|
|
padding: 1,
|
|
|
|
|
margin: 1,
|
|
|
|
|
borderStyle: "round",
|
|
|
|
|
borderColor: "white",
|
|
|
|
|
titleAlignment: "center",
|
|
|
|
|
}),
|
|
|
|
|
);
|
2025-02-17 15:13:47 +01:00
|
|
|
}
|
|
|
|
|
|
2025-02-20 10:04:58 +01:00
|
|
|
function getAvailableRoots() {
|
|
|
|
|
const devices = filesystemSync();
|
|
|
|
|
var mountPoints = [];
|
2025-02-25 13:59:26 +01:00
|
|
|
Object.keys(devices).forEach(function (key) {
|
|
|
|
|
var val = devices[key];
|
|
|
|
|
val.volumes.forEach(function (volume) {
|
|
|
|
|
mountPoints.push(volume.mountPoint);
|
2025-02-20 10:04:58 +01:00
|
|
|
});
|
2025-02-25 13:59:26 +01:00
|
|
|
});
|
2025-02-20 10:04:58 +01:00
|
|
|
|
|
|
|
|
if (mountPoints.length < 1) {
|
|
|
|
|
throw new Error("Failed to detect file system devices.");
|
|
|
|
|
}
|
|
|
|
|
return mountPoints;
|
|
|
|
|
}
|
2025-02-20 09:23:18 +01:00
|
|
|
|
2025-02-17 15:13:47 +01:00
|
|
|
function splitPath(str) {
|
|
|
|
|
return str.replaceAll("\\", "/").split("/");
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-19 14:56:22 +01:00
|
|
|
function dropEmptyParts(parts) {
|
|
|
|
|
var result = [];
|
2025-02-25 13:59:26 +01:00
|
|
|
parts.forEach(function (part) {
|
2025-02-19 14:56:22 +01:00
|
|
|
if (part.length > 0) {
|
|
|
|
|
result.push(part);
|
|
|
|
|
}
|
2025-02-25 13:59:26 +01:00
|
|
|
});
|
2025-02-19 14:56:22 +01:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function combine(parts) {
|
|
|
|
|
const toJoin = dropEmptyParts(parts);
|
|
|
|
|
if (toJoin.length == 1) return toJoin[0];
|
|
|
|
|
return path.join(...toJoin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function combineWith(parts, extra) {
|
|
|
|
|
const toJoin = dropEmptyParts(parts);
|
|
|
|
|
if (toJoin.length == 1) return path.join(toJoin[0], extra);
|
|
|
|
|
return path.join(...toJoin, extra);
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-17 15:13:47 +01:00
|
|
|
function showCurrent(currentPath) {
|
|
|
|
|
const len = currentPath.length;
|
2025-02-19 14:56:22 +01:00
|
|
|
showMsg(`Current path: [${len}]\n` + combine(currentPath));
|
2025-02-20 09:23:18 +01:00
|
|
|
|
|
|
|
|
if (len < 2) {
|
|
|
|
|
showMsg(
|
2025-02-25 13:59:26 +01:00
|
|
|
"Warning - Known issue:\n" +
|
|
|
|
|
"Path selection does not work in root paths on some platforms.\n" +
|
|
|
|
|
'Use "Enter path" or "Create new folder" to navigate and create folders\n' +
|
|
|
|
|
"if this is the case for you.",
|
2025-02-20 09:23:18 +01:00
|
|
|
);
|
|
|
|
|
}
|
2025-02-17 15:13:47 +01:00
|
|
|
}
|
|
|
|
|
|
2025-02-20 10:04:58 +01:00
|
|
|
function hasValidRoot(roots, checkPath) {
|
|
|
|
|
if (checkPath.length < 1) return false;
|
|
|
|
|
var result = false;
|
2025-02-25 13:59:26 +01:00
|
|
|
roots.forEach(function (root) {
|
2025-02-20 10:04:58 +01:00
|
|
|
if (root.toLowerCase() == checkPath[0].toLowerCase()) {
|
|
|
|
|
console.log("valid root: " + combine(checkPath));
|
|
|
|
|
result = true;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
if (!result) console.log("invalid root: " + combine(checkPath));
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-17 15:13:47 +01:00
|
|
|
async function showMain(currentPath) {
|
|
|
|
|
showCurrent(currentPath);
|
2025-02-25 13:59:26 +01:00
|
|
|
const { choice } = await inquirer
|
|
|
|
|
.prompt([
|
|
|
|
|
{
|
|
|
|
|
type: "list",
|
|
|
|
|
name: "choice",
|
|
|
|
|
message: "Select an option:",
|
2025-02-17 15:13:47 +01:00
|
|
|
choices: [
|
2025-02-25 13:59:26 +01:00
|
|
|
"1. Enter path",
|
|
|
|
|
"2. Go up one",
|
|
|
|
|
"3. Go down one",
|
|
|
|
|
"4. Create new folder here",
|
|
|
|
|
"5. Select this path",
|
|
|
|
|
"6. Cancel",
|
2025-02-17 15:13:47 +01:00
|
|
|
],
|
|
|
|
|
pageSize: 6,
|
2025-02-25 13:59:26 +01:00
|
|
|
loop: true,
|
|
|
|
|
},
|
|
|
|
|
])
|
|
|
|
|
.catch(() => {
|
|
|
|
|
handleExit();
|
|
|
|
|
return { choice: "6" };
|
|
|
|
|
});
|
2025-02-17 15:13:47 +01:00
|
|
|
|
|
|
|
|
return choice;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-19 14:56:22 +01:00
|
|
|
export async function showPathSelector(startingPath, pathMustExist) {
|
2025-02-20 10:04:58 +01:00
|
|
|
const roots = getAvailableRoots();
|
2025-02-19 14:56:22 +01:00
|
|
|
var currentPath = splitPath(startingPath);
|
2025-02-20 10:04:58 +01:00
|
|
|
if (!hasValidRoot(roots, currentPath)) {
|
|
|
|
|
currentPath = [roots[0]];
|
|
|
|
|
}
|
2025-02-17 15:13:47 +01:00
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
const choice = await showMain(currentPath);
|
|
|
|
|
|
2025-02-20 10:04:58 +01:00
|
|
|
var newCurrentPath = currentPath;
|
2025-02-25 13:59:26 +01:00
|
|
|
switch (choice.split(".")[0]) {
|
|
|
|
|
case "1":
|
|
|
|
|
newCurrentPath = await enterPath(currentPath, pathMustExist);
|
|
|
|
|
break;
|
|
|
|
|
case "2":
|
|
|
|
|
newCurrentPath = upOne(currentPath);
|
|
|
|
|
break;
|
|
|
|
|
case "3":
|
|
|
|
|
newCurrentPath = await downOne(currentPath);
|
|
|
|
|
break;
|
|
|
|
|
case "4":
|
|
|
|
|
newCurrentPath = await createSubDir(currentPath, pathMustExist);
|
|
|
|
|
break;
|
|
|
|
|
case "5":
|
2025-02-19 14:56:22 +01:00
|
|
|
if (pathMustExist && !isDir(combine(currentPath))) {
|
2025-02-17 15:13:47 +01:00
|
|
|
console.log("Current path does not exist.");
|
2025-02-19 14:56:22 +01:00
|
|
|
break;
|
2025-02-17 15:13:47 +01:00
|
|
|
} else {
|
2025-02-19 14:56:22 +01:00
|
|
|
return combine(currentPath);
|
2025-02-17 15:13:47 +01:00
|
|
|
}
|
2025-02-25 13:59:26 +01:00
|
|
|
case "6":
|
|
|
|
|
return combine(currentPath);
|
2025-02-17 15:13:47 +01:00
|
|
|
}
|
2025-02-20 10:04:58 +01:00
|
|
|
|
|
|
|
|
if (hasValidRoot(roots, newCurrentPath)) {
|
|
|
|
|
currentPath = newCurrentPath;
|
|
|
|
|
} else {
|
|
|
|
|
console.log("Selected path has no valid root.");
|
|
|
|
|
}
|
2025-02-17 15:13:47 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-19 14:56:22 +01:00
|
|
|
async function enterPath(currentPath, pathMustExist) {
|
2025-02-17 15:13:47 +01:00
|
|
|
const response = await inquirer.prompt([
|
|
|
|
|
{
|
2025-02-25 13:59:26 +01:00
|
|
|
type: "input",
|
|
|
|
|
name: "path",
|
|
|
|
|
message: "Enter Path:",
|
|
|
|
|
},
|
|
|
|
|
]);
|
2025-02-17 15:13:47 +01:00
|
|
|
|
2025-02-19 14:56:22 +01:00
|
|
|
const newPath = response.path;
|
|
|
|
|
if (pathMustExist && !isDir(newPath)) {
|
|
|
|
|
console.log("The entered path does not exist.");
|
|
|
|
|
return currentPath;
|
|
|
|
|
}
|
2025-02-17 15:13:47 +01:00
|
|
|
return splitPath(response.path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function upOne(currentPath) {
|
|
|
|
|
return currentPath.slice(0, currentPath.length - 1);
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-19 14:56:22 +01:00
|
|
|
export function isDir(dir) {
|
|
|
|
|
try {
|
|
|
|
|
return fs.lstatSync(dir).isDirectory();
|
|
|
|
|
} catch {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2025-02-17 15:13:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isSubDir(currentPath, entry) {
|
2025-02-19 14:56:22 +01:00
|
|
|
const newPath = combineWith(currentPath, entry);
|
2025-02-17 15:13:47 +01:00
|
|
|
return isDir(newPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getSubDirOptions(currentPath) {
|
2025-02-19 14:56:22 +01:00
|
|
|
const fullPath = combine(currentPath);
|
|
|
|
|
const entries = fs.readdirSync(fullPath);
|
2025-02-17 15:13:47 +01:00
|
|
|
var result = [];
|
|
|
|
|
var counter = 1;
|
2025-02-25 13:59:26 +01:00
|
|
|
entries.forEach(function (entry) {
|
2025-02-17 15:13:47 +01:00
|
|
|
if (isSubDir(currentPath, entry)) {
|
2025-02-25 13:59:26 +01:00
|
|
|
result.push(counter + ". " + entry);
|
2025-02-20 10:04:58 +01:00
|
|
|
counter = counter + 1;
|
2025-02-17 15:13:47 +01:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function downOne(currentPath) {
|
|
|
|
|
const options = getSubDirOptions(currentPath);
|
|
|
|
|
if (options.length == 0) {
|
|
|
|
|
console.log("There are no subdirectories here.");
|
|
|
|
|
return currentPath;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-25 13:59:26 +01:00
|
|
|
const { choice } = await inquirer
|
|
|
|
|
.prompt([
|
|
|
|
|
{
|
|
|
|
|
type: "list",
|
|
|
|
|
name: "choice",
|
|
|
|
|
message: "Select an subdir:",
|
2025-02-17 15:13:47 +01:00
|
|
|
choices: options,
|
|
|
|
|
pageSize: options.length,
|
2025-02-25 13:59:26 +01:00
|
|
|
loop: true,
|
|
|
|
|
},
|
|
|
|
|
])
|
|
|
|
|
.catch(() => {
|
|
|
|
|
return currentPath;
|
|
|
|
|
});
|
2025-02-17 15:13:47 +01:00
|
|
|
|
2025-02-25 13:59:26 +01:00
|
|
|
const subDir = choice.split(". ")[1];
|
2025-02-17 15:13:47 +01:00
|
|
|
return [...currentPath, subDir];
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-19 14:56:22 +01:00
|
|
|
async function createSubDir(currentPath, pathMustExist) {
|
2025-02-17 15:13:47 +01:00
|
|
|
const response = await inquirer.prompt([
|
|
|
|
|
{
|
2025-02-25 13:59:26 +01:00
|
|
|
type: "input",
|
|
|
|
|
name: "name",
|
|
|
|
|
message: "Enter name:",
|
|
|
|
|
},
|
|
|
|
|
]);
|
2025-02-17 15:13:47 +01:00
|
|
|
|
|
|
|
|
const name = response.name;
|
|
|
|
|
if (name.length < 1) return;
|
|
|
|
|
|
2025-02-19 14:56:22 +01:00
|
|
|
const fullDir = combineWith(currentPath, name);
|
|
|
|
|
if (pathMustExist && !isDir(fullDir)) {
|
|
|
|
|
fs.mkdirSync(fullDir);
|
|
|
|
|
}
|
2025-02-17 15:13:47 +01:00
|
|
|
return [...currentPath, name];
|
|
|
|
|
}
|