Module
Deno standard library
File
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.// Copyright the Browserify authors. MIT License.// Ported from https://github.com/browserify/path-browserify/// This module is browser compatible.import type { FormatInputPathObject, ParsedPath } from "./_interface.ts";import {  CHAR_BACKWARD_SLASH,  CHAR_COLON,  CHAR_DOT,  CHAR_QUESTION_MARK,} from "./_constants.ts";import {  _format,  assertPath,  encodeWhitespace,  isPathSeparator,  isWindowsDeviceRoot,  normalizeString,} from "./_util.ts";import { assert } from "../_util/assert.ts";export const sep = "\\";export const delimiter = ";";/** * Resolves path segments into a path * @param pathSegments to process to path */export function resolve(...pathSegments: string[]): string {  let resolvedDevice = "";  let resolvedTail = "";  let resolvedAbsolute = false;  for (let i = pathSegments.length - 1; i >= -1; i--) {    let path: string;    // deno-lint-ignore no-explicit-any    const { Deno } = globalThis as any;    if (i >= 0) {      path = pathSegments[i];    } else if (!resolvedDevice) {      if (typeof Deno?.cwd !== "function") {        throw new TypeError("Resolved a drive-letter-less path without a CWD.");      }      path = Deno.cwd();    } else {      if (        typeof Deno?.env?.get !== "function" || typeof Deno?.cwd !== "function"      ) {        throw new TypeError("Resolved a relative path without a CWD.");      }      path = Deno.cwd();      // Verify that a cwd was found and that it actually points      // to our drive. If not, default to the drive's root.      if (        path === undefined ||        path.slice(0, 3).toLowerCase() !== ${resolvedDevice.toLowerCase()}\\ ) { path = ${resolvedDevice}\\;      }    }    assertPath(path);    const len = path.length;    // Skip empty entries    if (len === 0) continue;    let rootEnd = 0;    let device = "";    let isAbsolute = false;    const code = path.charCodeAt(0);    // Try to match a root    if (len > 1) {      if (isPathSeparator(code)) {        // Possible UNC root        // If we started with a separator, we know we at least have an        // absolute path of some kind (UNC or otherwise)        isAbsolute = true;        if (isPathSeparator(path.charCodeAt(1))) {          // Matched double path separator at beginning          let j = 2;          let last = j;          // Match 1 or more non-path separators          for (; j < len; ++j) {            if (isPathSeparator(path.charCodeAt(j))) break;          }          if (j < len && j !== last) {            const firstPart = path.slice(last, j);            // Matched!            last = j;            // Match 1 or more path separators            for (; j < len; ++j) {              if (!isPathSeparator(path.charCodeAt(j))) break;            }            if (j < len && j !== last) {              // Matched!              last = j;              // Match 1 or more non-path separators              for (; j < len; ++j) {                if (isPathSeparator(path.charCodeAt(j))) break;              }              if (j === len) {                // We matched a UNC root only                device = \\\\${firstPart}\\${path.slice(last)};                rootEnd = j;              } else if (j !== last) {                // We matched a UNC root with leftovers                device = \\\\${firstPart}\\${path.slice(last, j)};                rootEnd = j;              }            }          }        } else {          rootEnd = 1;        }      } else if (isWindowsDeviceRoot(code)) {        // Possible device root        if (path.charCodeAt(1) === CHAR_COLON) {          device = path.slice(0, 2);          rootEnd = 2;          if (len > 2) {            if (isPathSeparator(path.charCodeAt(2))) {              // Treat separator following drive name as an absolute path              // indicator              isAbsolute = true;              rootEnd = 3;            }          }        }      }    } else if (isPathSeparator(code)) {      // path contains just a path separator      rootEnd = 1;      isAbsolute = true;    }    if (      device.length > 0 &&      resolvedDevice.length > 0 &&      device.toLowerCase() !== resolvedDevice.toLowerCase()    ) {      // This path points to another device so it is not applicable      continue;    }    if (resolvedDevice.length === 0 && device.length > 0) {      resolvedDevice = device;    }    if (!resolvedAbsolute) {      resolvedTail = ${path.slice(rootEnd)}\\${resolvedTail};      resolvedAbsolute = isAbsolute;    }    if (resolvedAbsolute && resolvedDevice.length > 0) break;  }  // At this point the path should be resolved to a full absolute path,  // but handle relative paths to be safe (might happen when process.cwd()  // fails)  // Normalize the tail path  resolvedTail = normalizeString(    resolvedTail,    !resolvedAbsolute,    "\\",    isPathSeparator,  );  return resolvedDevice + (resolvedAbsolute ? "\\" : "") + resolvedTail || ".";}/** * Normalizes a path * @param path to normalize */export function normalize(path: string): string {  assertPath(path);  const len = path.length;  if (len === 0) return ".";  let rootEnd = 0;  let device: string | undefined;  let isAbsolute = false;  const code = path.charCodeAt(0);  // Try to match a root  if (len > 1) {    if (isPathSeparator(code)) {      // Possible UNC root      // If we started with a separator, we know we at least have an absolute      // path of some kind (UNC or otherwise)      isAbsolute = true;      if (isPathSeparator(path.charCodeAt(1))) {        // Matched double path separator at beginning        let j = 2;        let last = j;        // Match 1 or more non-path separators        for (; j < len; ++j) {          if (isPathSeparator(path.charCodeAt(j))) break;        }        if (j < len && j !== last) {          const firstPart = path.slice(last, j);          // Matched!          last = j;          // Match 1 or more path separators          for (; j < len; ++j) {            if (!isPathSeparator(path.charCodeAt(j))) break;          }          if (j < len && j !== last) {            // Matched!            last = j;            // Match 1 or more non-path separators            for (; j < len; ++j) {              if (isPathSeparator(path.charCodeAt(j))) break;            }            if (j === len) {              // We matched a UNC root only              // Return the normalized version of the UNC root since there              // is nothing left to process              return \\\\${firstPart}\\${path.slice(last)}\\;            } else if (j !== last) {              // We matched a UNC root with leftovers              device = \\\\${firstPart}\\${path.slice(last, j)};              rootEnd = j;            }          }        }      } else {        rootEnd = 1;      }    } else if (isWindowsDeviceRoot(code)) {      // Possible device root      if (path.charCodeAt(1) === CHAR_COLON) {        device = path.slice(0, 2);        rootEnd = 2;        if (len > 2) {          if (isPathSeparator(path.charCodeAt(2))) {            // Treat separator following drive name as an absolute path            // indicator            isAbsolute = true;            rootEnd = 3;          }        }      }    }  } else if (isPathSeparator(code)) {    // path contains just a path separator, exit early to avoid unnecessary    // work    return "\\";  }  let tail: string;  if (rootEnd < len) {    tail = normalizeString(      path.slice(rootEnd),      !isAbsolute,      "\\",      isPathSeparator,    );  } else {    tail = "";  }  if (tail.length === 0 && !isAbsolute) tail = ".";  if (tail.length > 0 && isPathSeparator(path.charCodeAt(len - 1))) {    tail += "\\";  }  if (device === undefined) {    if (isAbsolute) {      if (tail.length > 0) return \\${tail}; else return "\\"; } else if (tail.length > 0) { return tail; } else { return ""; } } else if (isAbsolute) { if (tail.length > 0) return ${device}\\${tail}; else return ${device}\\;  } else if (tail.length > 0) {    return device + tail;  } else {    return device;  }}/** * Verifies whether path is absolute * @param path to verify */export function isAbsolute(path: string): boolean {  assertPath(path);  const len = path.length;  if (len === 0) return false;  const code = path.charCodeAt(0);  if (isPathSeparator(code)) {    return true;  } else if (isWindowsDeviceRoot(code)) {    // Possible device root    if (len > 2 && path.charCodeAt(1) === CHAR_COLON) {      if (isPathSeparator(path.charCodeAt(2))) return true;    }  }  return false;}/** * Join all given a sequence of paths,then normalizes the resulting path. * @param paths to be joined and normalized */export function join(...paths: string[]): string {  const pathsCount = paths.length;  if (pathsCount === 0) return ".";  let joined: string | undefined;  let firstPart: string | null = null;  for (let i = 0; i < pathsCount; ++i) {    const path = paths[i];    assertPath(path);    if (path.length > 0) {      if (joined === undefined) joined = firstPart = path;      else joined += \\${path}; } } if (joined === undefined) return "."; // Make sure that the joined path doesn't start with two slashes, because // normalize() will mistake it for an UNC path then. // // This step is skipped when it is very clear that the user actually // intended to point at an UNC path. This is assumed when the first // non-empty string arguments starts with exactly two slashes followed by // at least one more non-slash character. // // Note that for normalize() to treat a path as an UNC path it needs to // have at least 2 components, so we don't filter for that here. // This means that the user can use join to construct UNC paths from // a server name and a share name; for example: // path.join('//server', 'share') -> '\\\\server\\share\\') let needsReplace = true; let slashCount = 0; assert(firstPart != null); if (isPathSeparator(firstPart.charCodeAt(0))) { ++slashCount; const firstLen = firstPart.length; if (firstLen > 1) { if (isPathSeparator(firstPart.charCodeAt(1))) { ++slashCount; if (firstLen > 2) { if (isPathSeparator(firstPart.charCodeAt(2))) ++slashCount; else { // We matched a UNC path in the first part needsReplace = false; } } } } } if (needsReplace) { // Find any more consecutive slashes we need to replace for (; slashCount < joined.length; ++slashCount) { if (!isPathSeparator(joined.charCodeAt(slashCount))) break; } // Replace the slashes if needed if (slashCount >= 2) joined = \\${joined.slice(slashCount)};  }  return normalize(joined);}/** * It will solve the relative path from from to to, for instance: *  from = 'C:\\orandea\\test\\aaa' *  to = 'C:\\orandea\\impl\\bbb' * The output of the function should be: '..\\..\\impl\\bbb' * @param from relative path * @param to relative path */export function relative(from: string, to: string): string {  assertPath(from);  assertPath(to);  if (from === to) return "";  const fromOrig = resolve(from);  const toOrig = resolve(to);  if (fromOrig === toOrig) return "";  from = fromOrig.toLowerCase();  to = toOrig.toLowerCase();  if (from === to) return "";  // Trim any leading backslashes  let fromStart = 0;  let fromEnd = from.length;  for (; fromStart < fromEnd; ++fromStart) {    if (from.charCodeAt(fromStart) !== CHAR_BACKWARD_SLASH) break;  }  // Trim trailing backslashes (applicable to UNC paths only)  for (; fromEnd - 1 > fromStart; --fromEnd) {    if (from.charCodeAt(fromEnd - 1) !== CHAR_BACKWARD_SLASH) break;  }  const fromLen = fromEnd - fromStart;  // Trim any leading backslashes  let toStart = 0;  let toEnd = to.length;  for (; toStart < toEnd; ++toStart) {    if (to.charCodeAt(toStart) !== CHAR_BACKWARD_SLASH) break;  }  // Trim trailing backslashes (applicable to UNC paths only)  for (; toEnd - 1 > toStart; --toEnd) {    if (to.charCodeAt(toEnd - 1) !== CHAR_BACKWARD_SLASH) break;  }  const toLen = toEnd - toStart;  // Compare paths to find the longest common path from root  const length = fromLen < toLen ? fromLen : toLen;  let lastCommonSep = -1;  let i = 0;  for (; i <= length; ++i) {    if (i === length) {      if (toLen > length) {        if (to.charCodeAt(toStart + i) === CHAR_BACKWARD_SLASH) {          // We get here if from is the exact base path for to.          // For example: from='C:\\foo\\bar'; to='C:\\foo\\bar\\baz'          return toOrig.slice(toStart + i + 1);        } else if (i === 2) {          // We get here if from is the device root.          // For example: from='C:\\'; to='C:\\foo'          return toOrig.slice(toStart + i);        }      }      if (fromLen > length) {        if (from.charCodeAt(fromStart + i) === CHAR_BACKWARD_SLASH) {          // We get here if to is the exact base path for from.          // For example: from='C:\\foo\\bar'; to='C:\\foo'          lastCommonSep = i;        } else if (i === 2) {          // We get here if to is the device root.          // For example: from='C:\\foo\\bar'; to='C:\\'          lastCommonSep = 3;        }      }      break;    }    const fromCode = from.charCodeAt(fromStart + i);    const toCode = to.charCodeAt(toStart + i);    if (fromCode !== toCode) break;    else if (fromCode === CHAR_BACKWARD_SLASH) lastCommonSep = i;  }  // We found a mismatch before the first common path separator was seen, so  // return the original to.  if (i !== length && lastCommonSep === -1) {    return toOrig;  }  let out = "";  if (lastCommonSep === -1) lastCommonSep = 0;  // Generate the relative path based on the path difference between to and  // from  for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {    if (i === fromEnd || from.charCodeAt(i) === CHAR_BACKWARD_SLASH) {      if (out.length === 0) out += "..";      else out += "\\..";    }  }  // Lastly, append the rest of the destination (to) path that comes after  // the common path parts  if (out.length > 0) {    return out + toOrig.slice(toStart + lastCommonSep, toEnd);  } else {    toStart += lastCommonSep;    if (toOrig.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) ++toStart;    return toOrig.slice(toStart, toEnd);  }}/** * Resolves path to a namespace path * @param path to resolve to namespace */export function toNamespacedPath(path: string): string {  // Note: this will *probably* throw somewhere.  if (typeof path !== "string") return path;  if (path.length === 0) return "";  const resolvedPath = resolve(path);  if (resolvedPath.length >= 3) {    if (resolvedPath.charCodeAt(0) === CHAR_BACKWARD_SLASH) {      // Possible UNC root      if (resolvedPath.charCodeAt(1) === CHAR_BACKWARD_SLASH) {        const code = resolvedPath.charCodeAt(2);        if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) {          // Matched non-long UNC root, convert the path to a long UNC path          return \\\\?\\UNC\\${resolvedPath.slice(2)}; } } } else if (isWindowsDeviceRoot(resolvedPath.charCodeAt(0))) { // Possible device root if ( resolvedPath.charCodeAt(1) === CHAR_COLON && resolvedPath.charCodeAt(2) === CHAR_BACKWARD_SLASH ) { // Matched device root, convert the path to a long UNC path return \\\\?\\${resolvedPath};      }    }  }  return path;}/** * Return the directory name of a path. * @param path to determine name for */export function dirname(path: string): string {  assertPath(path);  const len = path.length;  if (len === 0) return ".";  let rootEnd = -1;  let end = -1;  let matchedSlash = true;  let offset = 0;  const code = path.charCodeAt(0);  // Try to match a root  if (len > 1) {    if (isPathSeparator(code)) {      // Possible UNC root      rootEnd = offset = 1;      if (isPathSeparator(path.charCodeAt(1))) {        // Matched double path separator at beginning        let j = 2;        let last = j;        // Match 1 or more non-path separators        for (; j < len; ++j) {          if (isPathSeparator(path.charCodeAt(j))) break;        }        if (j < len && j !== last) {          // Matched!          last = j;          // Match 1 or more path separators          for (; j < len; ++j) {            if (!isPathSeparator(path.charCodeAt(j))) break;          }          if (j < len && j !== last) {            // Matched!            last = j;            // Match 1 or more non-path separators            for (; j < len; ++j) {              if (isPathSeparator(path.charCodeAt(j))) break;            }            if (j === len) {              // We matched a UNC root only              return path;            }            if (j !== last) {              // We matched a UNC root with leftovers              // Offset by 1 to include the separator after the UNC root to              // treat it as a "normal root" on top of a (UNC) root              rootEnd = offset = j + 1;            }          }        }      }    } else if (isWindowsDeviceRoot(code)) {      // Possible device root      if (path.charCodeAt(1) === CHAR_COLON) {        rootEnd = offset = 2;        if (len > 2) {          if (isPathSeparator(path.charCodeAt(2))) rootEnd = offset = 3;        }      }    }  } else if (isPathSeparator(code)) {    // path contains just a path separator, exit early to avoid    // unnecessary work    return path;  }  for (let i = len - 1; i >= offset; --i) {    if (isPathSeparator(path.charCodeAt(i))) {      if (!matchedSlash) {        end = i;        break;      }    } else {      // We saw the first non-path separator      matchedSlash = false;    }  }  if (end === -1) {    if (rootEnd === -1) return ".";    else end = rootEnd;  }  return path.slice(0, end);}/** * Return the last portion of a path. Trailing directory separators are ignored. * @param path to process * @param ext of path directory */export function basename(path: string, ext = ""): string {  if (ext !== undefined && typeof ext !== "string") {    throw new TypeError('"ext" argument must be a string');  }  assertPath(path);  let start = 0;  let end = -1;  let matchedSlash = true;  let i: number;  // Check for a drive letter prefix so as not to mistake the following  // path separator as an extra separator at the end of the path that can be  // disregarded  if (path.length >= 2) {    const drive = path.charCodeAt(0);    if (isWindowsDeviceRoot(drive)) {      if (path.charCodeAt(1) === CHAR_COLON) start = 2;    }  }  if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {    if (ext.length === path.length && ext === path) return "";    let extIdx = ext.length - 1;    let firstNonSlashEnd = -1;    for (i = path.length - 1; i >= start; --i) {      const code = path.charCodeAt(i);      if (isPathSeparator(code)) {        // If we reached a path separator that was not part of a set of path        // separators at the end of the string, stop now        if (!matchedSlash) {          start = i + 1;          break;        }      } else {        if (firstNonSlashEnd === -1) {          // We saw the first non-path separator, remember this index in case          // we need it if the extension ends up not matching          matchedSlash = false;          firstNonSlashEnd = i + 1;        }        if (extIdx >= 0) {          // Try to match the explicit extension          if (code === ext.charCodeAt(extIdx)) {            if (--extIdx === -1) {              // We matched the extension, so mark this as the end of our path              // component              end = i;            }          } else {            // Extension does not match, so our result is the entire path            // component            extIdx = -1;            end = firstNonSlashEnd;          }        }      }    }    if (start === end) end = firstNonSlashEnd;    else if (end === -1) end = path.length;    return path.slice(start, end);  } else {    for (i = path.length - 1; i >= start; --i) {      if (isPathSeparator(path.charCodeAt(i))) {        // If we reached a path separator that was not part of a set of path        // separators at the end of the string, stop now        if (!matchedSlash) {          start = i + 1;          break;        }      } else if (end === -1) {        // We saw the first non-path separator, mark this as the end of our        // path component        matchedSlash = false;        end = i + 1;      }    }    if (end === -1) return "";    return path.slice(start, end);  }}/** * Return the extension of the path with leading period. * @param path with extension * @returns extension (ex. for file.ts returns .ts) */export function extname(path: string): string {  assertPath(path);  let start = 0;  let startDot = -1;  let startPart = 0;  let end = -1;  let matchedSlash = true;  // Track the state of characters (if any) we see before our first dot and  // after any path separator we find  let preDotState = 0;  // Check for a drive letter prefix so as not to mistake the following  // path separator as an extra separator at the end of the path that can be  // disregarded  if (    path.length >= 2 &&    path.charCodeAt(1) === CHAR_COLON &&    isWindowsDeviceRoot(path.charCodeAt(0))  ) {    start = startPart = 2;  }  for (let i = path.length - 1; i >= start; --i) {    const code = path.charCodeAt(i);    if (isPathSeparator(code)) {      // If we reached a path separator that was not part of a set of path      // separators at the end of the string, stop now      if (!matchedSlash) {        startPart = i + 1;        break;      }      continue;    }    if (end === -1) {      // We saw the first non-path separator, mark this as the end of our      // extension      matchedSlash = false;      end = i + 1;    }    if (code === CHAR_DOT) {      // If this is our first dot, mark it as the start of our extension      if (startDot === -1) startDot = i;      else if (preDotState !== 1) preDotState = 1;    } else if (startDot !== -1) {      // We saw a non-dot and non-path separator before our dot, so we should      // have a good chance at having a non-empty extension      preDotState = -1;    }  }  if (    startDot === -1 ||    end === -1 ||    // We saw a non-dot character immediately before the dot    preDotState === 0 ||    // The (right-most) trimmed path component is exactly '..'    (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)  ) {    return "";  }  return path.slice(startDot, end);}/** * Generate a path from FormatInputPathObject object. * @param pathObject with path */export function format(pathObject: FormatInputPathObject): string {  if (pathObject === null || typeof pathObject !== "object") {    throw new TypeError(      The "pathObject" argument must be of type Object. Received type ${typeof pathObject}, ); } return _format("\\", pathObject);}/** * Return a ParsedPath object of the path. * @param path to process */export function parse(path: string): ParsedPath { assertPath(path); const ret: ParsedPath = { root: "", dir: "", base: "", ext: "", name: "" }; const len = path.length; if (len === 0) return ret; let rootEnd = 0; let code = path.charCodeAt(0); // Try to match a root if (len > 1) { if (isPathSeparator(code)) { // Possible UNC root rootEnd = 1; if (isPathSeparator(path.charCodeAt(1))) { // Matched double path separator at beginning let j = 2; let last = j; // Match 1 or more non-path separators for (; j < len; ++j) { if (isPathSeparator(path.charCodeAt(j))) break; } if (j < len && j !== last) { // Matched! last = j; // Match 1 or more path separators for (; j < len; ++j) { if (!isPathSeparator(path.charCodeAt(j))) break; } if (j < len && j !== last) { // Matched! last = j; // Match 1 or more non-path separators for (; j < len; ++j) { if (isPathSeparator(path.charCodeAt(j))) break; } if (j === len) { // We matched a UNC root only rootEnd = j; } else if (j !== last) { // We matched a UNC root with leftovers rootEnd = j + 1; } } } } } else if (isWindowsDeviceRoot(code)) { // Possible device root if (path.charCodeAt(1) === CHAR_COLON) { rootEnd = 2; if (len > 2) { if (isPathSeparator(path.charCodeAt(2))) { if (len === 3) { // path contains just a drive root, exit early to avoid // unnecessary work ret.root = ret.dir = path; return ret; } rootEnd = 3; } } else { // path contains just a drive root, exit early to avoid // unnecessary work ret.root = ret.dir = path; return ret; } } } } else if (isPathSeparator(code)) { // path contains just a path separator, exit early to avoid // unnecessary work ret.root = ret.dir = path; return ret; } if (rootEnd > 0) ret.root = path.slice(0, rootEnd); let startDot = -1; let startPart = rootEnd; let end = -1; let matchedSlash = true; let i = path.length - 1; // Track the state of characters (if any) we see before our first dot and // after any path separator we find let preDotState = 0; // Get non-dir info for (; i >= rootEnd; --i) { code = path.charCodeAt(i); if (isPathSeparator(code)) { // If we reached a path separator that was not part of a set of path // separators at the end of the string, stop now if (!matchedSlash) { startPart = i + 1; break; } continue; } if (end === -1) { // We saw the first non-path separator, mark this as the end of our // extension matchedSlash = false; end = i + 1; } if (code === CHAR_DOT) { // If this is our first dot, mark it as the start of our extension if (startDot === -1) startDot = i; else if (preDotState !== 1) preDotState = 1; } else if (startDot !== -1) { // We saw a non-dot and non-path separator before our dot, so we should // have a good chance at having a non-empty extension preDotState = -1; } } if ( startDot === -1 || end === -1 || // We saw a non-dot character immediately before the dot preDotState === 0 || // The (right-most) trimmed path component is exactly '..' (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) ) { if (end !== -1) { ret.base = ret.name = path.slice(startPart, end); } } else { ret.name = path.slice(startPart, startDot); ret.base = path.slice(startPart, end); ret.ext = path.slice(startDot, end); } // If the directory is the root, use the entire root as the dir including // the trailing slash if any (C:\abc -> C:\). Otherwise, strip out the // trailing slash (C:\abc\def -> C:\abc). if (startPart > 0 && startPart !== rootEnd) { ret.dir = path.slice(0, startPart - 1); } else ret.dir = ret.root; return ret;}/** * Converts a file URL to a path string. * * ts * import { fromFileUrl } from "./win32.ts"; * fromFileUrl("file:///home/foo"); // "\\home\\foo" * fromFileUrl("file:///C:/Users/foo"); // "C:\\Users\\foo" * fromFileUrl("file://localhost/home/foo"); // "\\\\localhost\\home\\foo" *  * @param url of a file URL */export function fromFileUrl(url: string | URL): string { url = url instanceof URL ? url : new URL(url); if (url.protocol != "file:") { throw new TypeError("Must be a file URL."); } let path = decodeURIComponent( url.pathname.replace(/\//g, "\\").replace(/%(?![0-9A-Fa-f]{2})/g, "%25"), ).replace(/^\\*([A-Za-z]:)(\\|$)/, "$1\\"); if (url.hostname != "") { // Note: The URL implementation guarantees that the drive letter and // hostname are mutually exclusive. Otherwise it would not have been valid // to append the hostname and path like this. path = \\\\${url.hostname}${path}; } return path;}/** * Converts a path string to a file URL. * * ts * import { toFileUrl } from "./win32.ts"; * toFileUrl("\\home\\foo"); // new URL("file:///home/foo") * toFileUrl("C:\\Users\\foo"); // new URL("file:///C:/Users/foo") * toFileUrl("\\\\127.0.0.1\\home\\foo"); // new URL("file://127.0.0.1/home/foo") *  * @param path to convert to file URL */export function toFileUrl(path: string): URL { if (!isAbsolute(path)) { throw new TypeError("Must be an absolute path."); } const [, hostname, pathname] = path.match( /^(?:[/\\]{2}([^/\\]+)(?=[/\\](?:[^/\\]|$)))?(.*)/,  )!;  const url = new URL("file:///");  url.pathname = encodeWhitespace(pathname.replace(/%/g, "%25"));  if (hostname != null && hostname != "localhost") {    url.hostname = hostname;    if (!url.hostname) {      throw new TypeError("Invalid hostname.");    }  }  return url;}