crosspath
Version:
A wrapper around the path module that always normalizes to POSIX (including converting backslashes to forward slashes)
206 lines • 6.55 kB
JavaScript
/* eslint-disable prefer-rest-params,@typescript-eslint/no-unused-vars */
import path from "path";
import os from "os";
const LEADING_SLASH_BEFORE_DRIVE_LETTER_REGEXP = /(^\/\w:\/)|(^\/\w:\\)/;
const DRIVE_LETTER_PREFIX = path.parse(process.cwd()).root;
const DRIVE_LETTER_REGEXP = /^\w:/;
const PLATFORM = os.platform();
/**
* Returns true if the given path looks like a Windows path
*/
function looksLikeWindowsPath(p) {
return /\\/.test(p) || DRIVE_LETTER_REGEXP.test(p);
}
/**
* Ensures that the given path follows posix file names
*/
export function ensurePosix(p) {
const isExtendedLengthPath = /^\\\\\?\\/.test(p);
// eslint-disable-next-line no-control-regex
const hasNonAscii = /[^\u0000-\u0080]+/.test(p);
if (isExtendedLengthPath || hasNonAscii) {
return p;
}
return p.replace(/\\/g, "/");
}
export function urlToFilename(url) {
let urlString = typeof url === "string" ? url : url.pathname;
// If the URL doesn't start with the file protocol, return it as-is
if (!urlString.startsWith("file:")) {
return normalize(urlString);
}
if (urlString.startsWith(`file://`)) {
urlString = urlString.slice(`file://`.length);
}
// On Windows, it might be malformated and look like /C:\Users\...
if (LEADING_SLASH_BEFORE_DRIVE_LETTER_REGEXP.test(urlString)) {
urlString = urlString.slice(1);
}
return normalize(urlString);
}
/**
* Picks the most fitting path implementation based on the given argument
*/
function pickPathImplementation(args) {
if ([...args].some(argument => typeof argument === "string" && looksLikeWindowsPath(argument))) {
return path.win32;
}
return path;
}
/**
* Wraps the return value in ensurePosix
*/
function ensurePosixReturnValue(methodName, args) {
return ensurePosix(pickPathImplementation(args)[methodName](...args));
}
/**
* Wraps all string arguments in ensurePosix
*/
function ensurePosixArgument(methodName, args) {
return path.posix[methodName](...[...args].map(arg => (typeof arg === "string" ? ensurePosix(arg) : arg)));
}
/**
* Returns an object from a path string - the opposite of format().
*/
export function parse(p) {
return ensurePosixArgument("parse", arguments);
}
/**
* Return the extension of the path, from the last '.' to
* end of string in the last portion of the path. If there is no '.'
* in the last portion of the path or the first character of it is '.',
* then it returns an empty string
*/
export function extname(p) {
return ensurePosixArgument("extname", arguments);
}
/**
* Determines whether {path} is an absolute path. An absolute path will always resolve to the same location,
* regardless of the working directory.
*/
export function isAbsolute(p) {
return pickPathImplementation(arguments).isAbsolute(p);
}
/**
* Join all arguments together and normalize the resulting path.
* Arguments must be strings. In v0.8, non-string arguments were silently
* ignored. In v0.10 and up, an exception is thrown.
*/
export function join(...paths) {
return ensurePosixReturnValue("join", arguments);
}
/**
* Normalize a string path, reducing '..' and '.' parts.
* When multiple slashes are found, they're replaced by a single one;
* when the path contains a trailing slash, it is preserved
*/
export function normalize(p) {
const result = ensurePosixReturnValue("normalize", arguments);
if (p.startsWith("./") && !result.startsWith("./")) {
return "./" + result;
}
return result;
}
/**
* Solve the relative path from {from} to {to}. At times we have two absolute paths,
* and we need to derive the relative path from one to the other.
* This is actually the reverse transform of path.resolve
*/
export function relative(from, to) {
return ensurePosixReturnValue("relative", arguments);
}
/**
* The right-most parameter is considered {to}. Other parameters are considered an array of {from}.
* Starting from leftmost {from} parameter, resolves {to} to an absolute path.
* If {to} isn't already absolute, {from} arguments are prepended in right to left order,
* until an absolute path is found. If after using all {from} paths still no absolute path is found,
* the current working directory is used as well. The resulting path is normalized, and trailing
* slashes are removed unless the path gets resolved to the root directory
*/
export function resolve(...pathSegments) {
return ensurePosixReturnValue("resolve", arguments);
}
/**
* Return the directory name of a path. Similar to the Unix dirname command.
*/
export function dirname(p) {
return ensurePosixReturnValue("dirname", arguments);
}
/**
* Return the last portion of a path. Similar to the Unix basename command.
* Often used to extract the file name from a fully qualified path.
*/
export function basename(p, ext) {
return ensurePosixReturnValue("basename", arguments);
}
/**
* Returns a path string from an object - the opposite of parse().
*/
export function format(pP) {
return ensurePosixReturnValue("format", arguments);
}
/**
* The method is non-operational and always returns a POSIX-formatted path with no further modifications,
* as that is how this function generally works for all POSIX-based systems
*/
export function toNamespacedPath(p) {
return ensurePosixReturnValue("toNamespacedPath", arguments);
}
/**
* For some tooling, it is important that all absolute paths include the drive letter on Windows,
* even though the drive letter is technically optional
*/
export function includeDriveLetter(p) {
if (PLATFORM !== "win32")
return p;
if (DRIVE_LETTER_REGEXP.test(p))
return p;
if (p.startsWith(DRIVE_LETTER_PREFIX))
return p;
if (!path.win32.isAbsolute(path.win32.normalize(p)))
return p;
return path.win32.join(DRIVE_LETTER_PREFIX, p);
}
export const posix = {
sep: path.posix.sep,
delimiter: path.posix.delimiter,
parse,
isAbsolute,
join,
extname,
normalize,
relative,
resolve,
dirname,
basename,
format,
toNamespacedPath,
get posix() {
return this;
},
get win32() {
return win32;
}
};
export const win32 = {
...path.win32,
get posix() {
return posix;
},
get win32() {
return this;
}
};
/**
* The raw, unaltered path module
*/
export const native = path;
export default {
...posix,
posix,
win32,
native,
includeDriveLetter,
urlToFilename
};
//# sourceMappingURL=index.js.map