Source: utils.js

// --------------------------------------------------------------------------- //

import { parse } from 'path';
import { lstatSync } from 'fs';

// --------------------------------------------------------------------------- //

/**
 * Returns true if path leads to a file.
 *
 * Includes files without extensions.
 * Touches the file system and needs error handling.
 *
 * @function isFile
 * @param {string} _path - The path to be checked.
 * @return {boolean} True if path is a file, false if it is a directory.
 */

const isFile = _path => lstatSync(_path).isFile();
// re-write to distinguish error from folder
// } catch (err) {
//   // lstatSync throws an error if path doesn't exist
//   console.error('isFile(): path does not exist', err);
//   // should prob not return false.. (indicates _path is a folder)
//   throw Error(err);
// }
// };

// --------------------------------------------------------------------------- //

// do i need/even use this?

/**
 * Returns true if value exists and is a string.
 * *
 * @function isString
 * @param {string} _path - The value to be checked.
 * @return {boolean} True if string, false if not.
 */
const isString = _path => typeof _path === 'string' || _path instanceof String;

// --------------------------------------------------------------------------- //

/**
 * Returns true if value exists and is a cased letter.
 * Cleverly checks if both-cases of a char are different.
 * From: https://stackoverflow.com/a/32567789
 *
 * @function isLetter
 * @param {string} letter - The value to be checked.
 * @return {boolean} True if a cased letter, false if not.
 */

const isLetter = letter => letter.toLowerCase() != letter.toUpperCase();

// --------------------------------------------------------------------------- //

/**
 * Returns true if path root contains the operating system.
 *
 * Root may be represented as '/' or 'C:\'.
 *
 * @function inOS
 * @param {string} _path - The path to be checked.
 * @return {boolean} True if path is in OS, false if not.
 */
const inOS = _path => {
  const osRoot = '/';
  const pathRoot = parse(_path).root;

  try {
    return pathRoot && pathRoot[0] === osRoot;
  } catch (err) {
    console.error('inOS error:', err);
    // should prob not return false.. (indicates _path is not in OS)
    return false;
  }
};

// --------------------------------------------------------------------------- //

/**
 * Checks if path contains windows system directories.
 *
 * Assumes input to be a POSIX path on the operating system drive.
 *
 * @function isSystemPath
 * @param {string} _path - The path to be checked.
 * @return {boolean} Returns true for system paths, false if not.
 *
 * */
const isSystemPath = _path => {
  // could shift this entire section to glob options...
  // ...but then couldn't pass in cwd
  // this function expects cleaned input from --path and process.cwd()
  // would only be called if path is on C:\ drive

  // requires inOS() = true and cleanSysRoot() and posixPath()

  // console.log('isSystemPath _path:', _path);

  // added trim() to catch oddities with whitespace
  if (_path.trim() === '/') {
    console.error('altering files from root level of system is forbidden');
    return true;
  }

  // forbidden path segments
  const forbiddenPaths = [
    '/jim/Application Data',
    '/jim/AppData',
    '/jim/Local Settings'
  ];

  for (const forbiddenPath of forbiddenPaths) {
    if (_path.includes(forbiddenPath)) {
      console.error(
        `altering files inside '${forbiddenPath}' system path is forbidden`
      );
      return true;
    }
  }

  // forbidden root paths
  const forbiddenRootPaths = [
    '/Windows',
    '/ProgramData',
    '/Program Files',
    '/Program Files (x86)',
    '/Recovery',
    '/PerfLogs',
    '/$Recycle.Bin',
    '/$RECYCLE.BIN',
    '/Documents and Settings',
    '/System Volume Information'
  ];

  for (const forbiddenRootPath of forbiddenRootPaths) {
    if (_path.startsWith(forbiddenRootPath)) {
      console.error(
        `altering files inside '${forbiddenRootPath}' root system path is forbidden`
      );
      return true;
    }
  }

  // is not a root system path
  return false;
};

// --------------------------------------------------------------------------- //

/**
 * Returns true if path is safe to edit.
 *
 * If not in OS or if in OS but not in a system path
 *
 * Stops editing of windows operating system files.
 * Allows editing across different letter drives.
 * Wraps other functions from this module in conditional logic.
 *
 * @function isSafeWinPath
 * @param {string} _path - The path to be checked.
 * @return {boolean} True if path is safe, false if not.
 * */
const isSafeWinPath = _path =>
  !inOS(_path) || (inOS(_path) && !isSystemPath(_path));

// --------------------------------------------------------------------------- //

/**
 * Windows filesystem utility functions.
 *
 * @module Utils
 *
 * */
export { isFile, isString, isLetter, inOS, isSystemPath, isSafeWinPath };

// --------------------------------------------------------------------------- //