@stryke/path
Version:
A package containing various utilities that expand the functionality of NodeJs's built-in `path` module
125 lines (123 loc) • 4.51 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const require_regex = require('./regex.cjs');
const require_is_type = require('./is-type.cjs');
const require_slash = require('./slash.cjs');
//#region src/join-paths.ts
function normalizeWindowsPath(input = "") {
if (!input) return input;
return input.replace(/\\/g, "/").replace(require_regex.DRIVE_LETTER_START_REGEX, (r) => r.toUpperCase());
}
function correctPaths(path) {
if (!path || path.length === 0) return ".";
path = normalizeWindowsPath(path);
const isUNCPath = path.match(require_regex.UNC_REGEX);
const isPathAbsolute = require_is_type.isAbsolute(path);
const trailingSeparator = path[path.length - 1] === "/";
path = normalizeString(path, !isPathAbsolute);
if (path.length === 0) {
if (isPathAbsolute) return "/";
return trailingSeparator ? "./" : ".";
}
if (trailingSeparator) path += "/";
if (require_regex.DRIVE_LETTER_REGEX.test(path)) path += "/";
if (isUNCPath) {
if (!isPathAbsolute) return `//./${path}`;
return `//${path}`;
}
return isPathAbsolute && !require_is_type.isAbsolute(path) ? `/${path}` : path;
}
/**
* Joins all given path segments together using the platform-specific separator as a delimiter.
*
* @remarks
* Multiple segments can be provided as separate arguments. The resulting path is normalized to remove any redundant or unnecessary segments.
*
* @example
* ```ts
* import { joinPaths } from 'stryke/path';
*
* const fullPath = joinPaths('folder1', 'folder2', '..', 'folder3', 'file.txt');
* console.log(fullPath); // Output: 'folder1/folder3/file.txt'
*
* const absolutePath = joinPaths('/root', 'folder', '.', 'subfolder', 'file.txt');
* console.log(absolutePath); // Output: '/root/folder/subfolder/file.txt'
*
* const windowsPath = joinPaths('C:\\', 'Users', 'Public', '..', 'Documents', 'file.txt');
* console.log(windowsPath); // Output: 'C:/Users/Documents/file.txt'
*
* const uncPath = joinPaths('\\\\Server\\Share', 'Folder', 'File.txt');
* console.log(uncPath); // Output: '//Server/Share/Folder/File.txt'
* ```
*
* @param segments - The path segments to join.
* @returns The joined and normalized path string.
*/
function joinPaths(...segments) {
let result = "";
for (const segment of segments) if (segment && require_slash.slash(segment).replaceAll(/\//g, "") !== ".") {
if (result) if (require_slash.slash(segment).replaceAll(/\//g, "") === "..") result = require_slash.slash(result).replace(/\/+$/, "").replace(/\/*[^/]+$/, "");
else result = `${require_slash.slash(result).replace(/\/+$/, "")}/${require_slash.slash(segment).replace(/^\/+/, "")}`;
else if (require_slash.slash(segment).replaceAll(/\//g, "") !== "..") result = segment;
}
return correctPaths(result);
}
const join = joinPaths;
/**
* Resolves a string path, resolving '.' and '.' segments and allowing paths above the root.
*
* @param path - The path to normalize.
* @param allowAboveRoot - Whether to allow the resulting path to be above the root directory.
* @returns the normalized path string.
*/
function normalizeString(path, allowAboveRoot) {
let res = "";
let lastSegmentLength = 0;
let lastSlash = -1;
let dots = 0;
let char = null;
for (let index = 0; index <= path.length; ++index) {
if (index < path.length) char = path[index];
else if (char === "/") break;
else char = "/";
if (char === "/") {
if (lastSlash === index - 1 || dots === 1) {} else if (dots === 2) {
if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
if (res.length > 2) {
const lastSlashIndex = res.lastIndexOf("/");
if (lastSlashIndex === -1) {
res = "";
lastSegmentLength = 0;
} else {
res = res.slice(0, lastSlashIndex);
lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
}
lastSlash = index;
dots = 0;
continue;
} else if (res.length > 0) {
res = "";
lastSegmentLength = 0;
lastSlash = index;
dots = 0;
continue;
}
}
if (allowAboveRoot) {
res += res.length > 0 ? "/.." : "..";
lastSegmentLength = 2;
}
} else {
if (res.length > 0) res += `/${path.slice(lastSlash + 1, index)}`;
else res = path.slice(lastSlash + 1, index);
lastSegmentLength = index - lastSlash - 1;
}
lastSlash = index;
dots = 0;
} else if (char === "." && dots !== -1) ++dots;
else dots = -1;
}
return res;
}
//#endregion
exports.join = join;
exports.joinPaths = joinPaths;