gltf-pipeline
Version:
Content pipeline tools for optimizing glTF assets.
134 lines (121 loc) • 4.15 kB
JavaScript
;
const { Check, RuntimeError } = require("cesium");
const os = require("os");
const path = require("path");
const { domainToUnicode, URL } = require("url");
module.exports = {
fileURLToPath: fileURLToPath,
pathToFileURL: pathToFileURL,
};
const isWindows = os.platform() === "win32";
const forwardSlashRegEx = /\//g;
const CHAR_LOWERCASE_A = 97;
const CHAR_LOWERCASE_Z = 122;
const CHAR_FORWARD_SLASH = 47;
const CHAR_BACKWARD_SLASH = 92;
const percentRegEx = /%/g;
const backslashRegEx = /\\/g;
const newlineRegEx = /\n/g;
const carriageReturnRegEx = /\r/g;
const tabRegEx = /\t/g;
// The following function is copied from Node.js implementation of url module
// https://github.com/nodejs/node/blob/7237eaa3353aacf284289c8b59b0a5e0fa5744bb/lib/internal/url.js#L1345-L1383
// pathToFileURL & fileURLToPath were added in 10.12 so we want to maintain ability run under older versions.
function fileURLToPath(path) {
Check.defined("path", path);
if (typeof path === "string") {
path = new URL(path);
}
if (path.protocol !== "file:") {
throw new RuntimeError("Expected path.protocol to start with file:");
}
return isWindows ? getPathFromURLWin32(path) : getPathFromURLPosix(path);
}
function pathToFileURL(filepath) {
let resolved = path.resolve(filepath);
// path.resolve strips trailing slashes so we must add them back
const filePathLast = filepath.charCodeAt(filepath.length - 1);
if (
(filePathLast === CHAR_FORWARD_SLASH ||
(isWindows && filePathLast === CHAR_BACKWARD_SLASH)) &&
resolved[resolved.length - 1] !== path.sep
) {
resolved += "/";
}
const outURL = new URL("file://");
if (resolved.includes("%")) {
resolved = resolved.replace(percentRegEx, "%25");
}
// in posix, "/" is a valid character in paths
if (!isWindows && resolved.includes("\\")) {
resolved = resolved.replace(backslashRegEx, "%5C");
}
if (resolved.includes("\n")) {
resolved = resolved.replace(newlineRegEx, "%0A");
}
if (resolved.includes("\r")) {
resolved = resolved.replace(carriageReturnRegEx, "%0D");
}
if (resolved.includes("\t")) {
resolved = resolved.replace(tabRegEx, "%09");
}
outURL.pathname = resolved;
return outURL;
}
function getPathFromURLWin32(url) {
const hostname = url.hostname;
let pathname = url.pathname;
for (let n = 0; n < pathname.length; n++) {
if (pathname[n] === "%") {
const third = pathname.codePointAt(n + 2) | 0x20;
if (
(pathname[n + 1] === "2" && third === 102) || // 2f 2F /
(pathname[n + 1] === "5" && third === 99)
) {
// 5c 5C \
throw new RuntimeError(
"file URL must not include encoded \\ or / characters"
);
}
}
}
pathname = pathname.replace(forwardSlashRegEx, "\\");
pathname = decodeURIComponent(pathname);
if (hostname !== "") {
// If hostname is set, then we have a UNC path
// Pass the hostname through domainToUnicode just in case
// it is an IDN using punycode encoding. We do not need to worry
// about percent encoding because the URL parser will have
// already taken care of that for us. Note that this only
// causes IDNs with an appropriate `xn--` prefix to be decoded.
return `\\\\${domainToUnicode(hostname)}${pathname}`;
}
// Otherwise, it's a local path that requires a drive letter
const letter = pathname.codePointAt(1) | 0x20;
const sep = pathname[2];
if (
letter < CHAR_LOWERCASE_A ||
letter > CHAR_LOWERCASE_Z || // a..z A..Z
sep !== ":"
) {
throw new RuntimeError("file URL must be absolute");
}
return pathname.slice(1);
}
function getPathFromURLPosix(url) {
if (url.hostname !== "") {
throw new RuntimeError("Invalid platform");
}
const pathname = url.pathname;
for (let n = 0; n < pathname.length; n++) {
if (pathname[n] === "%") {
const third = pathname.codePointAt(n + 2) | 0x20;
if (pathname[n + 1] === "2" && third === 102) {
throw new RuntimeError(
"file URL must not include encoded \\ or / characters"
);
}
}
}
return decodeURIComponent(pathname);
}