四好公路
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

272 lines
7.2 KiB

3 years ago
"use strict";
let isWindows = /^win/.test(process.platform),
forwardSlashPattern = /\//g,
protocolPattern = /^(\w{2,}):\/\//i,
url = module.exports,
jsonPointerSlash = /~1/g,
jsonPointerTilde = /~0/g;
// RegExp patterns to URL-encode special characters in local filesystem paths
let urlEncodePatterns = [
/\?/g, "%3F",
/\#/g, "%23",
];
// RegExp patterns to URL-decode special characters for local filesystem paths
let urlDecodePatterns = [
/\%23/g, "#",
/\%24/g, "$",
/\%26/g, "&",
/\%2C/g, ",",
/\%40/g, "@"
];
exports.parse = require("url").parse;
exports.resolve = require("url").resolve;
/**
* Returns the current working directory (in Node) or the current page URL (in browsers).
*
* @returns {string}
*/
exports.cwd = function cwd () {
if (process.browser) {
return location.href;
}
let path = process.cwd();
let lastChar = path.slice(-1);
if (lastChar === "/" || lastChar === "\\") {
return path;
}
else {
return path + "/";
}
};
/**
* Returns the protocol of the given URL, or `undefined` if it has no protocol.
*
* @param {string} path
* @returns {?string}
*/
exports.getProtocol = function getProtocol (path) {
let match = protocolPattern.exec(path);
if (match) {
return match[1].toLowerCase();
}
};
/**
* Returns the lowercased file extension of the given URL,
* or an empty string if it has no extension.
*
* @param {string} path
* @returns {string}
*/
exports.getExtension = function getExtension (path) {
let lastDot = path.lastIndexOf(".");
if (lastDot >= 0) {
return url.stripQuery(path.substr(lastDot).toLowerCase());
}
return "";
};
/**
* Removes the query, if any, from the given path.
*
* @param {string} path
* @returns {string}
*/
exports.stripQuery = function stripQuery (path) {
let queryIndex = path.indexOf("?");
if (queryIndex >= 0) {
path = path.substr(0, queryIndex);
}
return path;
};
/**
* Returns the hash (URL fragment), of the given path.
* If there is no hash, then the root hash ("#") is returned.
*
* @param {string} path
* @returns {string}
*/
exports.getHash = function getHash (path) {
let hashIndex = path.indexOf("#");
if (hashIndex >= 0) {
return path.substr(hashIndex);
}
return "#";
};
/**
* Removes the hash (URL fragment), if any, from the given path.
*
* @param {string} path
* @returns {string}
*/
exports.stripHash = function stripHash (path) {
let hashIndex = path.indexOf("#");
if (hashIndex >= 0) {
path = path.substr(0, hashIndex);
}
return path;
};
/**
* Determines whether the given path is an HTTP(S) URL.
*
* @param {string} path
* @returns {boolean}
*/
exports.isHttp = function isHttp (path) {
let protocol = url.getProtocol(path);
if (protocol === "http" || protocol === "https") {
return true;
}
else if (protocol === undefined) {
// There is no protocol. If we're running in a browser, then assume it's HTTP.
return process.browser;
}
else {
// It's some other protocol, such as "ftp://", "mongodb://", etc.
return false;
}
};
/**
* Determines whether the given path is a filesystem path.
* This includes "file://" URLs.
*
* @param {string} path
* @returns {boolean}
*/
exports.isFileSystemPath = function isFileSystemPath (path) {
if (process.browser) {
// We're running in a browser, so assume that all paths are URLs.
// This way, even relative paths will be treated as URLs rather than as filesystem paths
return false;
}
let protocol = url.getProtocol(path);
return protocol === undefined || protocol === "file";
};
/**
* Converts a filesystem path to a properly-encoded URL.
*
* This is intended to handle situations where JSON Schema $Ref Parser is called
* with a filesystem path that contains characters which are not allowed in URLs.
*
* @example
* The following filesystem paths would be converted to the following URLs:
*
* <"!@#$%^&*+=?'>.json ==> %3C%22!@%23$%25%5E&*+=%3F\'%3E.json
* C:\\My Documents\\File (1).json ==> C:/My%20Documents/File%20(1).json
* file://Project #42/file.json ==> file://Project%20%2342/file.json
*
* @param {string} path
* @returns {string}
*/
exports.fromFileSystemPath = function fromFileSystemPath (path) {
// Step 1: On Windows, replace backslashes with forward slashes,
// rather than encoding them as "%5C"
if (isWindows) {
path = path.replace(/\\/g, "/");
}
// Step 2: `encodeURI` will take care of MOST characters
path = encodeURI(path);
// Step 3: Manually encode characters that are not encoded by `encodeURI`.
// This includes characters such as "#" and "?", which have special meaning in URLs,
// but are just normal characters in a filesystem path.
for (let i = 0; i < urlEncodePatterns.length; i += 2) {
path = path.replace(urlEncodePatterns[i], urlEncodePatterns[i + 1]);
}
return path;
};
/**
* Converts a URL to a local filesystem path.
*
* @param {string} path
* @param {boolean} [keepFileProtocol] - If true, then "file://" will NOT be stripped
* @returns {string}
*/
exports.toFileSystemPath = function toFileSystemPath (path, keepFileProtocol) {
// Step 1: `decodeURI` will decode characters such as Cyrillic characters, spaces, etc.
path = decodeURI(path);
// Step 2: Manually decode characters that are not decoded by `decodeURI`.
// This includes characters such as "#" and "?", which have special meaning in URLs,
// but are just normal characters in a filesystem path.
for (let i = 0; i < urlDecodePatterns.length; i += 2) {
path = path.replace(urlDecodePatterns[i], urlDecodePatterns[i + 1]);
}
// Step 3: If it's a "file://" URL, then format it consistently
// or convert it to a local filesystem path
let isFileUrl = path.substr(0, 7).toLowerCase() === "file://";
if (isFileUrl) {
// Strip-off the protocol, and the initial "/", if there is one
path = path[7] === "/" ? path.substr(8) : path.substr(7);
// insert a colon (":") after the drive letter on Windows
if (isWindows && path[1] === "/") {
path = path[0] + ":" + path.substr(1);
}
if (keepFileProtocol) {
// Return the consistently-formatted "file://" URL
path = "file:///" + path;
}
else {
// Convert the "file://" URL to a local filesystem path.
// On Windows, it will start with something like "C:/".
// On Posix, it will start with "/"
isFileUrl = false;
path = isWindows ? path : "/" + path;
}
}
// Step 4: Normalize Windows paths (unless it's a "file://" URL)
if (isWindows && !isFileUrl) {
// Replace forward slashes with backslashes
path = path.replace(forwardSlashPattern, "\\");
// Capitalize the drive letter
if (path.substr(1, 2) === ":\\") {
path = path[0].toUpperCase() + path.substr(1);
}
}
return path;
};
/**
* Converts a $ref pointer to a valid JSON Path.
*
* @param {string} pointer
* @returns {Array<number | string>}
*/
exports.safePointerToPath = function safePointerToPath (pointer) {
if (pointer.length <= 1 || pointer[0] !== "#" || pointer[1] !== "/") {
return [];
}
return pointer
.slice(2)
.split("/")
.map((value) => {
return decodeURIComponent(value)
.replace(jsonPointerSlash, "/")
.replace(jsonPointerTilde, "~");
});
};