file.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import type { ElectronFile } from "./types/file";
  2. /**
  3. * The two parts of a file name - the name itself, and an (optional) extension.
  4. *
  5. * The extension does not include the dot.
  6. */
  7. type FileNameComponents = [name: string, extension: string | undefined];
  8. /**
  9. * Split a filename into its components - the name itself, and the extension (if
  10. * any) - returning both. The dot is not included in either.
  11. *
  12. * For example, `foo-bar.png` will be split into ["foo-bar", "png"].
  13. *
  14. * See {@link fileNameFromComponents} for the inverse operation.
  15. */
  16. export const nameAndExtension = (fileName: string): FileNameComponents => {
  17. const i = fileName.lastIndexOf(".");
  18. // No extension
  19. if (i == -1) return [fileName, undefined];
  20. // A hidden file without an extension, e.g. ".gitignore"
  21. if (i == 0) return [fileName, undefined];
  22. // Both components present, just omit the dot.
  23. return [fileName.slice(0, i), fileName.slice(i + 1)];
  24. };
  25. /**
  26. * If the file name or path has an extension, return a lowercased version of it.
  27. *
  28. * This is handy when comparing the extension to a known set without worrying
  29. * about case sensitivity.
  30. *
  31. * See {@link nameAndExtension} for its more generic sibling.
  32. */
  33. export const lowercaseExtension = (
  34. fileNameOrPath: string,
  35. ): string | undefined => {
  36. // We rely on the implementation of nameAndExtension using lastIndexOf to
  37. // allow us to also work on paths.
  38. const [, ext] = nameAndExtension(fileNameOrPath);
  39. return ext?.toLowerCase();
  40. };
  41. /**
  42. * Construct a file name from its components (name and extension).
  43. *
  44. * Inverse of {@link nameAndExtension}.
  45. */
  46. export const fileNameFromComponents = (components: FileNameComponents) =>
  47. components.filter((x) => !!x).join(".");
  48. /**
  49. * Return the file name portion from the given {@link path}.
  50. *
  51. * This tries to emulate the UNIX `basename` command. In particular, any
  52. * trailing slashes on the path are trimmed, so this function can be used to get
  53. * the name of the directory too.
  54. *
  55. * The path is assumed to use POSIX separators ("/").
  56. */
  57. export const basename = (path: string) => {
  58. const pathComponents = path.split("/");
  59. for (let i = pathComponents.length - 1; i >= 0; i--)
  60. if (pathComponents[i] !== "") return pathComponents[i];
  61. return path;
  62. };
  63. /**
  64. * Return the directory portion from the given {@link path}.
  65. *
  66. * This tries to emulate the UNIX `dirname` command. In particular, any trailing
  67. * slashes on the path are trimmed, so this function can be used to get the path
  68. * leading up to a directory too.
  69. *
  70. * The path is assumed to use POSIX separators ("/").
  71. */
  72. export const dirname = (path: string) => {
  73. const pathComponents = path.split("/");
  74. while (pathComponents.pop() == "") {
  75. /* no-op */
  76. }
  77. return pathComponents.join("/");
  78. };
  79. /**
  80. * Return a short description of the given {@link fileOrPath} suitable for
  81. * helping identify it in log messages.
  82. */
  83. export const fopLabel = (fileOrPath: File | string) =>
  84. fileOrPath instanceof File ? `File(${fileOrPath.name})` : fileOrPath;
  85. export function getFileNameSize(file: File | ElectronFile) {
  86. return `${file.name}_${convertBytesToHumanReadable(file.size)}`;
  87. }
  88. export function convertBytesToHumanReadable(
  89. bytes: number,
  90. precision = 2,
  91. ): string {
  92. if (bytes === 0 || isNaN(bytes)) {
  93. return "0 MB";
  94. }
  95. const i = Math.floor(Math.log(bytes) / Math.log(1024));
  96. const sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
  97. return (bytes / Math.pow(1024, i)).toFixed(precision) + " " + sizes[i];
  98. }