Sindbad~EG File Manager
/**
* @fileoverview The Path class.
* @author Nicholas C. Zakas
*/
/* globals URL */
//-----------------------------------------------------------------------------
// Types
//-----------------------------------------------------------------------------
/** @typedef{import("@humanfs/types").HfsImpl} HfsImpl */
/** @typedef{import("@humanfs/types").HfsDirectoryEntry} HfsDirectoryEntry */
//-----------------------------------------------------------------------------
// Helpers
//-----------------------------------------------------------------------------
/**
* Normalizes a path to use forward slashes.
* @param {string} filePath The path to normalize.
* @returns {string} The normalized path.
*/
function normalizePath(filePath) {
let startIndex = 0;
let endIndex = filePath.length;
if (/[a-z]:\//i.test(filePath)) {
startIndex = 3;
}
if (filePath.startsWith("./")) {
startIndex = 2;
}
if (filePath.startsWith("/")) {
startIndex = 1;
}
if (filePath.endsWith("/")) {
endIndex = filePath.length - 1;
}
return filePath.slice(startIndex, endIndex).replace(/\\/g, "/");
}
/**
* Asserts that the given name is a non-empty string, no equal to "." or "..",
* and does not contain a forward slash or backslash.
* @param {string} name The name to check.
* @returns {void}
* @throws {TypeError} When name is not valid.
*/
function assertValidName(name) {
if (typeof name !== "string") {
throw new TypeError("name must be a string");
}
if (!name) {
throw new TypeError("name cannot be empty");
}
if (name === ".") {
throw new TypeError(`name cannot be "."`);
}
if (name === "..") {
throw new TypeError(`name cannot be ".."`);
}
if (name.includes("/") || name.includes("\\")) {
throw new TypeError(
`name cannot contain a slash or backslash: "${name}"`,
);
}
}
//-----------------------------------------------------------------------------
// Exports
//-----------------------------------------------------------------------------
export class Path {
/**
* The steps in the path.
* @type {Array<string>}
*/
#steps;
/**
* Creates a new instance.
* @param {Iterable<string>} [steps] The steps to use for the path.
* @throws {TypeError} When steps is not iterable.
*/
constructor(steps = []) {
if (typeof steps[Symbol.iterator] !== "function") {
throw new TypeError("steps must be iterable");
}
this.#steps = [...steps];
this.#steps.forEach(assertValidName);
}
/**
* Adds steps to the end of the path.
* @param {...string} steps The steps to add to the path.
* @returns {void}
*/
push(...steps) {
steps.forEach(assertValidName);
this.#steps.push(...steps);
}
/**
* Removes the last step from the path.
* @returns {string} The last step in the path.
*/
pop() {
return this.#steps.pop();
}
/**
* Returns an iterator for steps in the path.
* @returns {IterableIterator<string>} An iterator for the steps in the path.
*/
steps() {
return this.#steps.values();
}
/**
* Returns an iterator for the steps in the path.
* @returns {IterableIterator<string>} An iterator for the steps in the path.
*/
[Symbol.iterator]() {
return this.steps();
}
/**
* Retrieves the name (the last step) of the path.
* @type {string}
*/
get name() {
return this.#steps[this.#steps.length - 1];
}
/**
* Sets the name (the last step) of the path.
* @type {string}
*/
set name(value) {
assertValidName(value);
this.#steps[this.#steps.length - 1] = value;
}
/**
* Retrieves the size of the path.
* @type {number}
*/
get size() {
return this.#steps.length;
}
/**
* Returns the path as a string.
* @returns {string} The path as a string.
*/
toString() {
return this.#steps.join("/");
}
/**
* Creates a new path based on the argument type. If the argument is a string,
* it is assumed to be a file or directory path and is converted to a Path
* instance. If the argument is a URL, it is assumed to be a file URL and is
* converted to a Path instance. If the argument is a Path instance, it is
* copied into a new Path instance. If the argument is an array, it is assumed
* to be the steps of a path and is used to create a new Path instance.
* @param {string|URL|Path|Array<string>} pathish The value to convert to a Path instance.
* @returns {Path} A new Path instance.
* @throws {TypeError} When pathish is not a string, URL, Path, or Array.
* @throws {TypeError} When pathish is a string and is empty.
*/
static from(pathish) {
if (typeof pathish === "string") {
if (!pathish) {
throw new TypeError("argument cannot be empty");
}
return Path.fromString(pathish);
}
if (pathish instanceof URL) {
return Path.fromURL(pathish);
}
if (pathish instanceof Path || Array.isArray(pathish)) {
return new Path(pathish);
}
throw new TypeError("argument must be a string, URL, Path, or Array");
}
/**
* Creates a new Path instance from a string.
* @param {string} fileOrDirPath The file or directory path to convert.
* @returns {Path} A new Path instance.
* @deprecated Use Path.from() instead.
*/
static fromString(fileOrDirPath) {
return new Path(normalizePath(fileOrDirPath).split("/"));
}
/**
* Creates a new Path instance from a URL.
* @param {URL} url The URL to convert.
* @returns {Path} A new Path instance.
* @throws {TypeError} When url is not a URL instance.
* @throws {TypeError} When url.pathname is empty.
* @throws {TypeError} When url.protocol is not "file:".
* @deprecated Use Path.from() instead.
*/
static fromURL(url) {
if (!(url instanceof URL)) {
throw new TypeError("url must be a URL instance");
}
if (!url.pathname || url.pathname === "/") {
throw new TypeError("url.pathname cannot be empty");
}
if (url.protocol !== "file:") {
throw new TypeError(`url.protocol must be "file:"`);
}
// Remove leading slash in pathname
return new Path(normalizePath(url.pathname.slice(1)).split("/"));
}
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists