204 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			204 lines
		
	
	
		
			5.1 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,
 | 
						|
});
 | 
						|
 | 
						|
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;
 | 
						|
 | 
						|
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;
 | 
						|
 | 
						|
/**
 | 
						|
 * @param {string} maybePath a path
 | 
						|
 * @returns {PathType} type of path
 | 
						|
 */
 | 
						|
const getType = (maybePath) => {
 | 
						|
	switch (maybePath.length) {
 | 
						|
		case 0:
 | 
						|
			return PathType.Empty;
 | 
						|
		case 1: {
 | 
						|
			const c0 = maybePath.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 = maybePath.charCodeAt(0);
 | 
						|
			switch (c0) {
 | 
						|
				case CHAR_DOT: {
 | 
						|
					const c1 = maybePath.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 = maybePath.charCodeAt(1);
 | 
						|
			if (
 | 
						|
				c1 === CHAR_COLON &&
 | 
						|
				((c0 >= CHAR_A && c0 <= CHAR_Z) ||
 | 
						|
					(c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z))
 | 
						|
			) {
 | 
						|
				return PathType.AbsoluteWin;
 | 
						|
			}
 | 
						|
			return PathType.Normal;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	const c0 = maybePath.charCodeAt(0);
 | 
						|
	switch (c0) {
 | 
						|
		case CHAR_DOT: {
 | 
						|
			const c1 = maybePath.charCodeAt(1);
 | 
						|
			switch (c1) {
 | 
						|
				case CHAR_SLASH:
 | 
						|
					return PathType.Relative;
 | 
						|
				case CHAR_DOT: {
 | 
						|
					const c2 = maybePath.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 = maybePath.charCodeAt(1);
 | 
						|
	if (c1 === CHAR_COLON) {
 | 
						|
		const c2 = maybePath.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;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * @param {string} maybePath a path
 | 
						|
 * @returns {string} the normalized path
 | 
						|
 */
 | 
						|
const normalize = (maybePath) => {
 | 
						|
	switch (getType(maybePath)) {
 | 
						|
		case PathType.Empty:
 | 
						|
			return maybePath;
 | 
						|
		case PathType.AbsoluteWin:
 | 
						|
			return winNormalize(maybePath);
 | 
						|
		case PathType.Relative: {
 | 
						|
			const r = posixNormalize(maybePath);
 | 
						|
			return getType(r) === PathType.Relative ? r : `./${r}`;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return posixNormalize(maybePath);
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * @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);
 | 
						|
};
 | 
						|
 | 
						|
/** @type {Map<string, Map<string, string | undefined>>} */
 | 
						|
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;
 | 
						|
};
 | 
						|
 | 
						|
module.exports.PathType = PathType;
 | 
						|
module.exports.cachedJoin = cachedJoin;
 | 
						|
module.exports.deprecatedInvalidSegmentRegEx = deprecatedInvalidSegmentRegEx;
 | 
						|
module.exports.getType = getType;
 | 
						|
module.exports.invalidSegmentRegEx = invalidSegmentRegEx;
 | 
						|
module.exports.join = join;
 | 
						|
module.exports.normalize = normalize;
 |