UNPKG

@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
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;