2025-02-17 15:13:47 +01:00
|
|
|
import path from 'path';
|
|
|
|
|
import inquirer from 'inquirer';
|
|
|
|
|
import boxen from 'boxen';
|
|
|
|
|
import chalk from 'chalk';
|
|
|
|
|
import fs from 'fs';
|
2025-02-20 09:23:18 +01:00
|
|
|
import { filesystemSync } from 'fs-filesystem';
|
2025-02-17 15:13:47 +01:00
|
|
|
|
|
|
|
|
function showMsg(msg) {
|
|
|
|
|
console.log(boxen(chalk.white(msg), {
|
|
|
|
|
padding: 1,
|
|
|
|
|
margin: 1,
|
|
|
|
|
borderStyle: 'round',
|
|
|
|
|
borderColor: 'white',
|
|
|
|
|
titleAlignment: 'center'
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-20 10:04:58 +01:00
|
|
|
function getAvailableRoots() {
|
|
|
|
|
const devices = filesystemSync();
|
|
|
|
|
var mountPoints = [];
|
|
|
|
|
Object.keys(devices).forEach(function(key) {
|
|
|
|
|
var val = devices[key];
|
|
|
|
|
val.volumes.forEach(function(volume) {
|
|
|
|
|
mountPoints.push(volume.mountPoint);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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 = [];
|
|
|
|
|
parts.forEach(function(part) {
|
|
|
|
|
if (part.length > 0) {
|
|
|
|
|
result.push(part);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
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(
|
|
|
|
|
'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-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;
|
|
|
|
|
roots.forEach(function(root) {
|
|
|
|
|
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);
|
|
|
|
|
const { choice } = await inquirer.prompt([
|
|
|
|
|
{
|
|
|
|
|
type: 'list',
|
|
|
|
|
name: 'choice',
|
|
|
|
|
message: 'Select an option:',
|
|
|
|
|
choices: [
|
|
|
|
|
'1. Enter path',
|
|
|
|
|
'2. Go up one',
|
|
|
|
|
'3. Go down one',
|
2025-02-19 14:56:22 +01:00
|
|
|
'4. Create new folder here',
|
|
|
|
|
'5. Select this path',
|
|
|
|
|
'6. Cancel'
|
2025-02-17 15:13:47 +01:00
|
|
|
],
|
|
|
|
|
pageSize: 6,
|
|
|
|
|
loop: true
|
|
|
|
|
}
|
|
|
|
|
]).catch(() => {
|
|
|
|
|
handleExit();
|
|
|
|
|
return { choice: '6' };
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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-17 15:13:47 +01:00
|
|
|
switch (choice.split('.')[0]) {
|
|
|
|
|
case '1':
|
2025-02-20 10:04:58 +01:00
|
|
|
newCurrentPath = await enterPath(currentPath, pathMustExist);
|
2025-02-17 15:13:47 +01:00
|
|
|
break;
|
|
|
|
|
case '2':
|
2025-02-20 10:04:58 +01:00
|
|
|
newCurrentPath = upOne(currentPath);
|
2025-02-17 15:13:47 +01:00
|
|
|
break;
|
|
|
|
|
case '3':
|
2025-02-20 10:04:58 +01:00
|
|
|
newCurrentPath = await downOne(currentPath);
|
2025-02-17 15:13:47 +01:00
|
|
|
break;
|
|
|
|
|
case '4':
|
2025-02-20 10:04:58 +01:00
|
|
|
newCurrentPath = await createSubDir(currentPath, pathMustExist);
|
2025-02-17 15:13:47 +01:00
|
|
|
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-19 14:56:22 +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([
|
|
|
|
|
{
|
|
|
|
|
type: 'input',
|
|
|
|
|
name: 'path',
|
|
|
|
|
message: 'Enter Path:'
|
|
|
|
|
}]);
|
|
|
|
|
|
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;
|
|
|
|
|
entries.forEach(function(entry) {
|
|
|
|
|
if (isSubDir(currentPath, entry)) {
|
2025-02-20 10:04:58 +01:00
|
|
|
result.push(counter + '. ' + entry);
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { choice } = await inquirer.prompt([
|
|
|
|
|
{
|
|
|
|
|
type: 'list',
|
|
|
|
|
name: 'choice',
|
|
|
|
|
message: 'Select an subdir:',
|
|
|
|
|
choices: options,
|
|
|
|
|
pageSize: options.length,
|
|
|
|
|
loop: true
|
|
|
|
|
}
|
|
|
|
|
]).catch(() => {
|
|
|
|
|
return currentPath;
|
|
|
|
|
});
|
|
|
|
|
|
2025-02-20 10:04:58 +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([
|
|
|
|
|
{
|
|
|
|
|
type: 'input',
|
|
|
|
|
name: 'name',
|
|
|
|
|
message: 'Enter name:'
|
|
|
|
|
}]);
|
|
|
|
|
|
|
|
|
|
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];
|
|
|
|
|
}
|