222 lines
5.0 KiB
JavaScript
222 lines
5.0 KiB
JavaScript
/*
|
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
Author Tobias Koppers @sokra
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
const path = require("path");
|
|
|
|
const CHAR_HASH = "#".charCodeAt(0);
|
|
const CHAR_SLASH = "/".charCodeAt(0);
|
|
const CHAR_BACKSLASH = "\\".charCodeAt(0);
|
|
const CHAR_A = "A".charCodeAt(0);
|
|
const CHAR_Z = "Z".charCodeAt(0);
|
|
const CHAR_LOWER_A = "a".charCodeAt(0);
|
|
const CHAR_LOWER_Z = "z".charCodeAt(0);
|
|
const CHAR_DOT = ".".charCodeAt(0);
|
|
const CHAR_COLON = ":".charCodeAt(0);
|
|
|
|
const posixNormalize = path.posix.normalize;
|
|
const winNormalize = path.win32.normalize;
|
|
|
|
/**
|
|
* @enum {number}
|
|
*/
|
|
const PathType = Object.freeze({
|
|
Empty: 0,
|
|
Normal: 1,
|
|
Relative: 2,
|
|
AbsoluteWin: 3,
|
|
AbsolutePosix: 4,
|
|
Internal: 5
|
|
});
|
|
exports.PathType = PathType;
|
|
|
|
/**
|
|
* @param {string} p a path
|
|
* @returns {PathType} type of path
|
|
*/
|
|
const getType = p => {
|
|
switch (p.length) {
|
|
case 0:
|
|
return PathType.Empty;
|
|
case 1: {
|
|
const c0 = p.charCodeAt(0);
|
|
switch (c0) {
|
|
case CHAR_DOT:
|
|
return PathType.Relative;
|
|
case CHAR_SLASH:
|
|
return PathType.AbsolutePosix;
|
|
case CHAR_HASH:
|
|
return PathType.Internal;
|
|
}
|
|
return PathType.Normal;
|
|
}
|
|
case 2: {
|
|
const c0 = p.charCodeAt(0);
|
|
switch (c0) {
|
|
case CHAR_DOT: {
|
|
const c1 = p.charCodeAt(1);
|
|
switch (c1) {
|
|
case CHAR_DOT:
|
|
case CHAR_SLASH:
|
|
return PathType.Relative;
|
|
}
|
|
return PathType.Normal;
|
|
}
|
|
case CHAR_SLASH:
|
|
return PathType.AbsolutePosix;
|
|
case CHAR_HASH:
|
|
return PathType.Internal;
|
|
}
|
|
const c1 = p.charCodeAt(1);
|
|
if (c1 === CHAR_COLON) {
|
|
if (
|
|
(c0 >= CHAR_A && c0 <= CHAR_Z) ||
|
|
(c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z)
|
|
) {
|
|
return PathType.AbsoluteWin;
|
|
}
|
|
}
|
|
return PathType.Normal;
|
|
}
|
|
}
|
|
const c0 = p.charCodeAt(0);
|
|
switch (c0) {
|
|
case CHAR_DOT: {
|
|
const c1 = p.charCodeAt(1);
|
|
switch (c1) {
|
|
case CHAR_SLASH:
|
|
return PathType.Relative;
|
|
case CHAR_DOT: {
|
|
const c2 = p.charCodeAt(2);
|
|
if (c2 === CHAR_SLASH) return PathType.Relative;
|
|
return PathType.Normal;
|
|
}
|
|
}
|
|
return PathType.Normal;
|
|
}
|
|
case CHAR_SLASH:
|
|
return PathType.AbsolutePosix;
|
|
case CHAR_HASH:
|
|
return PathType.Internal;
|
|
}
|
|
const c1 = p.charCodeAt(1);
|
|
if (c1 === CHAR_COLON) {
|
|
const c2 = p.charCodeAt(2);
|
|
if (
|
|
(c2 === CHAR_BACKSLASH || c2 === CHAR_SLASH) &&
|
|
((c0 >= CHAR_A && c0 <= CHAR_Z) ||
|
|
(c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z))
|
|
) {
|
|
return PathType.AbsoluteWin;
|
|
}
|
|
}
|
|
return PathType.Normal;
|
|
};
|
|
exports.getType = getType;
|
|
|
|
/**
|
|
* @param {string} p a path
|
|
* @returns {string} the normalized path
|
|
*/
|
|
const normalize = p => {
|
|
switch (getType(p)) {
|
|
case PathType.Empty:
|
|
return p;
|
|
case PathType.AbsoluteWin:
|
|
return winNormalize(p);
|
|
case PathType.Relative: {
|
|
const r = posixNormalize(p);
|
|
return getType(r) === PathType.Relative ? r : `./${r}`;
|
|
}
|
|
}
|
|
return posixNormalize(p);
|
|
};
|
|
exports.normalize = normalize;
|
|
|
|
/**
|
|
* @param {string} rootPath the root path
|
|
* @param {string | undefined} request the request path
|
|
* @returns {string} the joined path
|
|
*/
|
|
const join = (rootPath, request) => {
|
|
if (!request) return normalize(rootPath);
|
|
const requestType = getType(request);
|
|
switch (requestType) {
|
|
case PathType.AbsolutePosix:
|
|
return posixNormalize(request);
|
|
case PathType.AbsoluteWin:
|
|
return winNormalize(request);
|
|
}
|
|
switch (getType(rootPath)) {
|
|
case PathType.Normal:
|
|
case PathType.Relative:
|
|
case PathType.AbsolutePosix:
|
|
return posixNormalize(`${rootPath}/${request}`);
|
|
case PathType.AbsoluteWin:
|
|
return winNormalize(`${rootPath}\\${request}`);
|
|
}
|
|
switch (requestType) {
|
|
case PathType.Empty:
|
|
return rootPath;
|
|
case PathType.Relative: {
|
|
const r = posixNormalize(rootPath);
|
|
return getType(r) === PathType.Relative ? r : `./${r}`;
|
|
}
|
|
}
|
|
return posixNormalize(rootPath);
|
|
};
|
|
exports.join = join;
|
|
|
|
const joinCache = new Map();
|
|
|
|
/**
|
|
* @param {string} rootPath the root path
|
|
* @param {string | undefined} request the request path
|
|
* @returns {string} the joined path
|
|
*/
|
|
const cachedJoin = (rootPath, request) => {
|
|
let cacheEntry;
|
|
let cache = joinCache.get(rootPath);
|
|
if (cache === undefined) {
|
|
joinCache.set(rootPath, (cache = new Map()));
|
|
} else {
|
|
cacheEntry = cache.get(request);
|
|
if (cacheEntry !== undefined) return cacheEntry;
|
|
}
|
|
cacheEntry = join(rootPath, request);
|
|
cache.set(request, cacheEntry);
|
|
return cacheEntry;
|
|
};
|
|
exports.cachedJoin = cachedJoin;
|
|
|
|
const checkExportsFieldTarget = relativePath => {
|
|
let lastNonSlashIndex = 2;
|
|
let slashIndex = relativePath.indexOf("/", 2);
|
|
let cd = 0;
|
|
|
|
while (slashIndex !== -1) {
|
|
const folder = relativePath.slice(lastNonSlashIndex, slashIndex);
|
|
|
|
switch (folder) {
|
|
case "..": {
|
|
cd--;
|
|
if (cd < 0)
|
|
return new Error(
|
|
`Trying to access out of package scope. Requesting ${relativePath}`
|
|
);
|
|
break;
|
|
}
|
|
default:
|
|
cd++;
|
|
break;
|
|
}
|
|
|
|
lastNonSlashIndex = slashIndex + 1;
|
|
slashIndex = relativePath.indexOf("/", lastNonSlashIndex);
|
|
}
|
|
};
|
|
exports.checkExportsFieldTarget = checkExportsFieldTarget;
|