1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-23 15:16:54 -05:00

Clean up path module (denoland/deno_std#149)

Original: 47e6fc775a
This commit is contained in:
Max Graey 2019-01-24 21:06:24 +02:00 committed by Ryan Dahl
parent deedb5e84e
commit 57b6fdb2eb
3 changed files with 97 additions and 125 deletions

View file

@ -3,8 +3,6 @@
import { platform } from "deno"; import { platform } from "deno";
const isWindows = platform.os === "win";
// Alphabet chars. // Alphabet chars.
export const CHAR_UPPERCASE_A = 65; /* A */ export const CHAR_UPPERCASE_A = 65; /* A */
export const CHAR_LOWERCASE_A = 97; /* a */ export const CHAR_LOWERCASE_A = 97; /* a */
@ -50,4 +48,5 @@ export const CHAR_EQUAL = 61; /* = */
export const CHAR_0 = 48; /* 0 */ export const CHAR_0 = 48; /* 0 */
export const CHAR_9 = 57; /* 9 */ export const CHAR_9 = 57; /* 9 */
export const isWindows = platform.os === "win";
export const EOL = isWindows ? "\r\n" : "\n"; export const EOL = isWindows ? "\r\n" : "\n";

View file

@ -23,25 +23,5 @@ export interface ParsedPath {
*/ */
name: string; name: string;
} }
export interface FormatInputPathObject {
/** export type FormatInputPathObject = Partial<ParsedPath>
* The root of the path such as '/' or 'c:\'
*/
root?: string;
/**
* The full directory path such as '/home/user/dir' or 'c:\path\dir'
*/
dir?: string;
/**
* The file name including extension (if any) such as 'index.html'
*/
base?: string;
/**
* The file extension (if any) such as '.html'
*/
ext?: string;
/**
* The file name without extension (if any) such as 'index'
*/
name?: string;
}

View file

@ -1,7 +1,10 @@
// Copyright the Browserify authors. MIT License. // Copyright the Browserify authors. MIT License.
// Ported from https://github.com/browserify/path-browserify/ // Ported from https://github.com/browserify/path-browserify/
import { cwd, env } from "deno";
import { FormatInputPathObject, ParsedPath } from "./interface.ts";
import { import {
isWindows,
CHAR_UPPERCASE_A, CHAR_UPPERCASE_A,
CHAR_LOWERCASE_A, CHAR_LOWERCASE_A,
CHAR_UPPERCASE_Z, CHAR_UPPERCASE_Z,
@ -12,10 +15,8 @@ import {
CHAR_COLON, CHAR_COLON,
CHAR_QUESTION_MARK CHAR_QUESTION_MARK
} from "./constants.ts"; } from "./constants.ts";
import { cwd, env, platform } from "deno";
import { FormatInputPathObject, ParsedPath } from "./interface.ts";
function assertPath(path: string) { function assertPath(path: string): void {
if (typeof path !== "string") { if (typeof path !== "string") {
throw new TypeError( throw new TypeError(
`Path must be a string. Received ${JSON.stringify(path)}` `Path must be a string. Received ${JSON.stringify(path)}`
@ -23,18 +24,18 @@ function assertPath(path: string) {
} }
} }
function isPathSeparator(code: number) { function isPosixPathSeparator(code: number): boolean {
return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
}
function isPosixPathSeparator(code: number) {
return code === CHAR_FORWARD_SLASH; return code === CHAR_FORWARD_SLASH;
} }
function isWindowsDeviceRoot(code: number) { function isPathSeparator(code: number): boolean {
return isPosixPathSeparator(code) || code === CHAR_BACKWARD_SLASH;
}
function isWindowsDeviceRoot(code: number): boolean {
return ( return (
(code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) || (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z) ||
(code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z) (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z)
); );
} }
@ -44,14 +45,14 @@ function normalizeString(
allowAboveRoot: boolean, allowAboveRoot: boolean,
separator: string, separator: string,
isPathSeparator: (code: number) => boolean isPathSeparator: (code: number) => boolean
) { ): string {
let res = ""; let res = "";
let lastSegmentLength = 0; let lastSegmentLength = 0;
let lastSlash = -1; let lastSlash = -1;
let dots = 0; let dots = 0;
let code: number; let code: number;
for (let i = 0; i <= path.length; ++i) { for (let i = 0, len = path.length; i <= len; ++i) {
if (i < path.length) code = path.charCodeAt(i); if (i < len) code = path.charCodeAt(i);
else if (isPathSeparator(code)) break; else if (isPathSeparator(code)) break;
else code = CHAR_FORWARD_SLASH; else code = CHAR_FORWARD_SLASH;
@ -106,30 +107,26 @@ function normalizeString(
return res; return res;
} }
function _format(sep: string, pathObject: FormatInputPathObject) { function _format(sep: string, pathObject: FormatInputPathObject): string {
const dir = pathObject.dir || pathObject.root; const dir: string | null = pathObject.dir || pathObject.root;
const base = const base: string =
pathObject.base || (pathObject.name || "") + (pathObject.ext || ""); pathObject.base || (pathObject.name || "") + (pathObject.ext || "");
if (!dir) { if (!dir) return base;
return base; if (dir === pathObject.root) return dir + base;
}
if (dir === pathObject.root) {
return dir + base;
}
return dir + sep + base; return dir + sep + base;
} }
export const win32 = { export const win32 = {
// path.resolve([from ...], to) // path.resolve([from ...], to)
resolve: function resolve(...pathSegments: string[]) { resolve(...pathSegments: string[]): string {
let resolvedDevice = ""; let resolvedDevice = "";
let resolvedTail = ""; let resolvedTail = "";
let resolvedAbsolute = false; let resolvedAbsolute = false;
for (let i = arguments.length - 1; i >= -1; i--) { for (let i = pathSegments.length - 1; i >= -1; i--) {
let path: string; let path: string;
if (i >= 0) { if (i >= 0) {
path = arguments[i]; path = pathSegments[i];
} else if (!resolvedDevice) { } else if (!resolvedDevice) {
path = cwd(); path = cwd();
} else { } else {
@ -152,12 +149,11 @@ export const win32 = {
assertPath(path); assertPath(path);
// Skip empty entries const len = path.length;
if (path.length === 0) {
continue; // Skip empty entries
} if (len === 0) continue;
let len = path.length;
let rootEnd = 0; let rootEnd = 0;
let device = ""; let device = "";
let isAbsolute = false; let isAbsolute = false;
@ -249,9 +245,7 @@ export const win32 = {
resolvedAbsolute = isAbsolute; resolvedAbsolute = isAbsolute;
} }
if (resolvedDevice.length > 0 && resolvedAbsolute) { if (resolvedAbsolute && resolvedDevice.length > 0) break;
break;
}
} }
// At this point the path should be resolved to a full absolute path, // At this point the path should be resolved to a full absolute path,
@ -271,7 +265,7 @@ export const win32 = {
); );
}, },
normalize: function normalize(path: string) { normalize(path: string): string {
assertPath(path); assertPath(path);
const len = path.length; const len = path.length;
if (len === 0) return "."; if (len === 0) return ".";
@ -384,7 +378,7 @@ export const win32 = {
} }
}, },
isAbsolute: function isAbsolute(path: string) { isAbsolute(path: string): boolean {
assertPath(path); assertPath(path);
const len = path.length; const len = path.length;
if (len === 0) return false; if (len === 0) return false;
@ -402,17 +396,18 @@ export const win32 = {
return false; return false;
}, },
join: function join(...paths: string[]) { join(...paths: string[]): string {
if (arguments.length === 0) return "."; const pathsCount = paths.length;
if (pathsCount === 0) return ".";
let joined: string; let joined: string;
let firstPart: string; let firstPart: string;
for (let i = 0; i < arguments.length; ++i) { for (let i = 0; i < pathsCount; ++i) {
let arg = arguments[i]; let path = paths[i];
assertPath(arg); assertPath(path);
if (arg.length > 0) { if (path.length > 0) {
if (joined === undefined) joined = firstPart = arg; if (joined === undefined) joined = firstPart = path;
else joined += `\\${arg}`; else joined += `\\${path}`;
} }
} }
@ -466,7 +461,7 @@ export const win32 = {
// from = 'C:\\orandea\\test\\aaa' // from = 'C:\\orandea\\test\\aaa'
// to = 'C:\\orandea\\impl\\bbb' // to = 'C:\\orandea\\impl\\bbb'
// The output of the function should be: '..\\..\\impl\\bbb' // The output of the function should be: '..\\..\\impl\\bbb'
relative: function relative(from: string, to: string) { relative(from: string, to: string): string {
assertPath(from); assertPath(from);
assertPath(to); assertPath(to);
@ -484,11 +479,11 @@ export const win32 = {
// Trim any leading backslashes // Trim any leading backslashes
let fromStart = 0; let fromStart = 0;
for (; fromStart < from.length; ++fromStart) { let fromEnd = from.length;
for (; fromStart < fromEnd; ++fromStart) {
if (from.charCodeAt(fromStart) !== CHAR_BACKWARD_SLASH) break; if (from.charCodeAt(fromStart) !== CHAR_BACKWARD_SLASH) break;
} }
// Trim trailing backslashes (applicable to UNC paths only) // Trim trailing backslashes (applicable to UNC paths only)
let fromEnd = from.length;
for (; fromEnd - 1 > fromStart; --fromEnd) { for (; fromEnd - 1 > fromStart; --fromEnd) {
if (from.charCodeAt(fromEnd - 1) !== CHAR_BACKWARD_SLASH) break; if (from.charCodeAt(fromEnd - 1) !== CHAR_BACKWARD_SLASH) break;
} }
@ -496,11 +491,11 @@ export const win32 = {
// Trim any leading backslashes // Trim any leading backslashes
let toStart = 0; let toStart = 0;
for (; toStart < to.length; ++toStart) { let toEnd = to.length;
for (; toStart < toEnd; ++toStart) {
if (to.charCodeAt(toStart) !== CHAR_BACKWARD_SLASH) break; if (to.charCodeAt(toStart) !== CHAR_BACKWARD_SLASH) break;
} }
// Trim trailing backslashes (applicable to UNC paths only) // Trim trailing backslashes (applicable to UNC paths only)
let toEnd = to.length;
for (; toEnd - 1 > toStart; --toEnd) { for (; toEnd - 1 > toStart; --toEnd) {
if (to.charCodeAt(toEnd - 1) !== CHAR_BACKWARD_SLASH) break; if (to.charCodeAt(toEnd - 1) !== CHAR_BACKWARD_SLASH) break;
} }
@ -570,13 +565,10 @@ export const win32 = {
} }
}, },
toNamespacedPath: function toNamespacedPath(path: string) { toNamespacedPath(path: string): string {
// Note: this will *probably* throw somewhere. // Note: this will *probably* throw somewhere.
if (typeof path !== "string") return path; if (typeof path !== "string") return path;
if (path.length === 0) return "";
if (path.length === 0) {
return "";
}
const resolvedPath = win32.resolve(path); const resolvedPath = win32.resolve(path);
@ -607,7 +599,7 @@ export const win32 = {
return path; return path;
}, },
dirname: function dirname(path: string) { dirname(path: string): string {
assertPath(path); assertPath(path);
const len = path.length; const len = path.length;
if (len === 0) return "."; if (len === 0) return ".";
@ -695,10 +687,12 @@ export const win32 = {
return path.slice(0, end); return path.slice(0, end);
}, },
basename: function basename(path: string, ext = "") { basename(path: string, ext = ""): string {
if (ext !== undefined && typeof ext !== "string") if (ext !== undefined && typeof ext !== "string")
throw new TypeError('"ext" argument must be a string'); throw new TypeError('"ext" argument must be a string');
assertPath(path); assertPath(path);
let start = 0; let start = 0;
let end = -1; let end = -1;
let matchedSlash = true; let matchedSlash = true;
@ -777,7 +771,7 @@ export const win32 = {
} }
}, },
extname: function extname(path: string) { extname(path: string): string {
assertPath(path); assertPath(path);
let start = 0; let start = 0;
let startDot = -1; let startDot = -1;
@ -841,7 +835,7 @@ export const win32 = {
return path.slice(startDot, end); return path.slice(startDot, end);
}, },
format: function format(pathObject: FormatInputPathObject) { format(pathObject: FormatInputPathObject): string {
if (pathObject === null || typeof pathObject !== "object") { if (pathObject === null || typeof pathObject !== "object") {
throw new TypeError( throw new TypeError(
`The "pathObject" argument must be of type Object. Received type ${typeof pathObject}` `The "pathObject" argument must be of type Object. Received type ${typeof pathObject}`
@ -850,13 +844,14 @@ export const win32 = {
return _format("\\", pathObject); return _format("\\", pathObject);
}, },
parse: function parse(path: string) { parse(path: string): ParsedPath {
assertPath(path); assertPath(path);
let ret = { root: "", dir: "", base: "", ext: "", name: "" } as ParsedPath; let ret = { root: "", dir: "", base: "", ext: "", name: "" } as ParsedPath;
if (path.length === 0) return ret;
let len = path.length; const len = path.length;
if (len === 0) return ret;
let rootEnd = 0; let rootEnd = 0;
let code = path.charCodeAt(0); let code = path.charCodeAt(0);
@ -991,9 +986,9 @@ export const win32 = {
// If the directory is the root, use the entire root as the `dir` including // 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 // the trailing slash if any (`C:\abc` -> `C:\`). Otherwise, strip out the
// trailing slash (`C:\abc\def` -> `C:\abc`). // trailing slash (`C:\abc\def` -> `C:\abc`).
if (startPart > 0 && startPart !== rootEnd) if (startPart > 0 && startPart !== rootEnd) {
ret.dir = path.slice(0, startPart - 1); ret.dir = path.slice(0, startPart - 1);
else ret.dir = ret.root; } else ret.dir = ret.root;
return ret; return ret;
}, },
@ -1006,16 +1001,15 @@ export const win32 = {
export const posix = { export const posix = {
// path.resolve([from ...], to) // path.resolve([from ...], to)
resolve: function resolve(...pathSegments: string[]) { resolve(...pathSegments: string[]): string {
let resolvedPath = ""; let resolvedPath = "";
let resolvedAbsolute = false; let resolvedAbsolute = false;
for (let i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { for (let i = pathSegments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
let path: string; let path: string;
if (i >= 0) path = arguments[i];
else { if (i >= 0) path = pathSegments[i];
path = cwd(); else path = cwd();
}
assertPath(path); assertPath(path);
@ -1042,14 +1036,11 @@ export const posix = {
if (resolvedAbsolute) { if (resolvedAbsolute) {
if (resolvedPath.length > 0) return `/${resolvedPath}`; if (resolvedPath.length > 0) return `/${resolvedPath}`;
else return "/"; else return "/";
} else if (resolvedPath.length > 0) { } else if (resolvedPath.length > 0) return resolvedPath;
return resolvedPath; else return ".";
} else {
return ".";
}
}, },
normalize: function normalize(path: string) { normalize(path: string): string {
assertPath(path); assertPath(path);
if (path.length === 0) return "."; if (path.length === 0) return ".";
@ -1068,27 +1059,27 @@ export const posix = {
return path; return path;
}, },
isAbsolute: function isAbsolute(path: string) { isAbsolute(path: string): boolean {
assertPath(path); assertPath(path);
return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH; return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH;
}, },
join: function join(...paths: string[]) { join(...paths: string[]): string {
if (arguments.length === 0) return "."; if (paths.length === 0) return ".";
let joined: string; let joined: string | undefined;
for (let i = 0; i < arguments.length; ++i) { for (let i = 0, len = paths.length; i < len; ++i) {
let arg = arguments[i]; let path = paths[i];
assertPath(arg); assertPath(path);
if (arg.length > 0) { if (path.length > 0) {
if (joined === undefined) joined = arg; if (!joined) joined = path;
else joined += `/${arg}`; else joined += `/${path}`;
} }
} }
if (joined === undefined) return "."; if (!joined) return ".";
return posix.normalize(joined); return posix.normalize(joined);
}, },
relative: function relative(from: string, to: string) { relative(from: string, to: string): string {
assertPath(from); assertPath(from);
assertPath(to); assertPath(to);
@ -1101,18 +1092,18 @@ export const posix = {
// Trim any leading backslashes // Trim any leading backslashes
let fromStart = 1; let fromStart = 1;
for (; fromStart < from.length; ++fromStart) { let fromEnd = from.length;
for (; fromStart < fromEnd; ++fromStart) {
if (from.charCodeAt(fromStart) !== CHAR_FORWARD_SLASH) break; if (from.charCodeAt(fromStart) !== CHAR_FORWARD_SLASH) break;
} }
let fromEnd = from.length;
let fromLen = fromEnd - fromStart; let fromLen = fromEnd - fromStart;
// Trim any leading backslashes // Trim any leading backslashes
let toStart = 1; let toStart = 1;
for (; toStart < to.length; ++toStart) { const toEnd = to.length;
for (; toStart < toEnd; ++toStart) {
if (to.charCodeAt(toStart) !== CHAR_FORWARD_SLASH) break; if (to.charCodeAt(toStart) !== CHAR_FORWARD_SLASH) break;
} }
let toEnd = to.length;
let toLen = toEnd - toStart; let toLen = toEnd - toStart;
// Compare paths to find the longest common path from root // Compare paths to find the longest common path from root
@ -1170,12 +1161,12 @@ export const posix = {
} }
}, },
toNamespacedPath: function toNamespacedPath(path: string) { toNamespacedPath(path: string): string {
// Non-op on posix systems // Non-op on posix systems
return path; return path;
}, },
dirname: function dirname(path: string) { dirname(path: string): string {
assertPath(path); assertPath(path);
if (path.length === 0) return "."; if (path.length === 0) return ".";
const hasRoot = path.charCodeAt(0) === CHAR_FORWARD_SLASH; const hasRoot = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
@ -1198,7 +1189,7 @@ export const posix = {
return path.slice(0, end); return path.slice(0, end);
}, },
basename: function basename(path: string, ext = "") { basename(path: string, ext = ""): string {
if (ext !== undefined && typeof ext !== "string") if (ext !== undefined && typeof ext !== "string")
throw new TypeError('"ext" argument must be a string'); throw new TypeError('"ext" argument must be a string');
assertPath(path); assertPath(path);
@ -1271,7 +1262,7 @@ export const posix = {
} }
}, },
extname: function extname(path: string) { extname(path: string): string {
assertPath(path); assertPath(path);
let startDot = -1; let startDot = -1;
let startPart = 0; let startPart = 0;
@ -1321,7 +1312,7 @@ export const posix = {
return path.slice(startDot, end); return path.slice(startDot, end);
}, },
format: function format(pathObject: FormatInputPathObject) { format(pathObject: FormatInputPathObject): string {
if (pathObject === null || typeof pathObject !== "object") { if (pathObject === null || typeof pathObject !== "object") {
throw new TypeError( throw new TypeError(
`The "pathObject" argument must be of type Object. Received type ${typeof pathObject}` `The "pathObject" argument must be of type Object. Received type ${typeof pathObject}`
@ -1330,7 +1321,7 @@ export const posix = {
return _format("/", pathObject); return _format("/", pathObject);
}, },
parse: function parse(path: string) { parse(path: string): ParsedPath {
assertPath(path); assertPath(path);
let ret = { root: "", dir: "", base: "", ext: "", name: "" } as ParsedPath; let ret = { root: "", dir: "", base: "", ext: "", name: "" } as ParsedPath;
@ -1391,9 +1382,11 @@ export const posix = {
(preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)
) { ) {
if (end !== -1) { if (end !== -1) {
if (startPart === 0 && isAbsolute) if (startPart === 0 && isAbsolute) {
ret.base = ret.name = path.slice(1, end); ret.base = ret.name = path.slice(1, end);
else ret.base = ret.name = path.slice(startPart, end); } else {
ret.base = ret.name = path.slice(startPart, end);
}
} }
} else { } else {
if (startPart === 0 && isAbsolute) { if (startPart === 0 && isAbsolute) {
@ -1421,7 +1414,7 @@ export const posix = {
posix.win32 = win32.win32 = win32; posix.win32 = win32.win32 = win32;
posix.posix = win32.posix = posix; posix.posix = win32.posix = posix;
const module = platform.os === "win" ? win32 : posix; const module = isWindows ? win32 : posix;
export const resolve = module.resolve; export const resolve = module.resolve;
export const normalize = module.normalize; export const normalize = module.normalize;