/* 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; const invalidSegmentRegEx = /(^|\\|\/)((\.|%2e)(\.|%2e)?|(n|%6e|%4e)(o|%6f|%4f)(d|%64|%44)(e|%65|%45)(_|%5f)(m|%6d|%4d)(o|%6f|%4f)(d|%64|%44)(u|%75|%55)(l|%6c|%4c)(e|%65|%45)(s|%73|%53))?(\\|\/|$)/i; exports.invalidSegmentRegEx = invalidSegmentRegEx; const deprecatedInvalidSegmentRegEx = /(^|\\|\/)((\.|%2e)(\.|%2e)?|(n|%6e|%4e)(o|%6f|%4f)(d|%64|%44)(e|%65|%45)(_|%5f)(m|%6d|%4d)(o|%6f|%4f)(d|%64|%44)(u|%75|%55)(l|%6c|%4c)(e|%65|%45)(s|%73|%53))(\\|\/|$)/i; exports.deprecatedInvalidSegmentRegEx = deprecatedInvalidSegmentRegEx; /** * @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; /** @type {Map>} */ const joinCache = new Map(); /** * @param {string} rootPath the root path * @param {string} request the request path * @returns {string} the joined path */ const cachedJoin = (rootPath, request) => { /** @type {string | undefined} */ 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;