| 
									
										
										
										
											2018-07-30 23:08:51 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-05 21:38:15 +08:00
										 |  |  | "use strict"; | 
					
						
							| 
									
										
										
										
											2018-07-30 23:08:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-05 21:38:15 +08:00
										 |  |  | const path = require("path"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | const WINDOWS_ABS_PATH_REGEXP = /^[a-zA-Z]:[\\/]/; | 
					
						
							| 
									
										
										
										
											2020-03-13 00:51:26 +08:00
										 |  |  | const SEGMENTS_SPLIT_REGEXP = /([|!])/; | 
					
						
							| 
									
										
										
										
											2018-12-26 17:40:42 +08:00
										 |  |  | const WINDOWS_PATH_SEPARATOR_REGEXP = /\\/g; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-21 05:37:42 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} MakeRelativePathsCache | 
					
						
							|  |  |  |  * @property {Map<string, Map<string, string>>=} relativePaths | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-04-15 05:31:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-01 04:51:59 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @param {string} relativePath relative path | 
					
						
							|  |  |  |  * @returns {string} request | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-08-19 20:01:44 +08:00
										 |  |  | const relativePathToRequest = relativePath => { | 
					
						
							|  |  |  | 	if (relativePath === "") return "./."; | 
					
						
							|  |  |  | 	if (relativePath === "..") return "../."; | 
					
						
							|  |  |  | 	if (relativePath.startsWith("../")) return relativePath; | 
					
						
							|  |  |  | 	return `./${relativePath}`; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-15 05:31:11 +08:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2018-12-08 01:12:04 +08:00
										 |  |  |  * @param {string} context context for relative path | 
					
						
							|  |  |  |  * @param {string} maybeAbsolutePath path to make relative | 
					
						
							|  |  |  |  * @returns {string} relative path in request style | 
					
						
							| 
									
										
										
										
											2018-04-15 05:31:11 +08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-12-08 01:12:04 +08:00
										 |  |  | const absoluteToRequest = (context, maybeAbsolutePath) => { | 
					
						
							| 
									
										
										
										
											2018-12-23 20:22:07 +08:00
										 |  |  | 	if (maybeAbsolutePath[0] === "/") { | 
					
						
							|  |  |  | 		if ( | 
					
						
							|  |  |  | 			maybeAbsolutePath.length > 1 && | 
					
						
							|  |  |  | 			maybeAbsolutePath[maybeAbsolutePath.length - 1] === "/" | 
					
						
							|  |  |  | 		) { | 
					
						
							|  |  |  | 			// this 'path' is actually a regexp generated by dynamic requires.
 | 
					
						
							|  |  |  | 			// Don't treat it as an absolute path.
 | 
					
						
							|  |  |  | 			return maybeAbsolutePath; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-26 17:40:42 +08:00
										 |  |  | 		const querySplitPos = maybeAbsolutePath.indexOf("?"); | 
					
						
							|  |  |  | 		let resource = | 
					
						
							|  |  |  | 			querySplitPos === -1 | 
					
						
							|  |  |  | 				? maybeAbsolutePath | 
					
						
							|  |  |  | 				: maybeAbsolutePath.slice(0, querySplitPos); | 
					
						
							| 
									
										
										
										
											2021-08-19 20:01:44 +08:00
										 |  |  | 		resource = relativePathToRequest(path.posix.relative(context, resource)); | 
					
						
							| 
									
										
										
										
											2018-12-26 17:40:42 +08:00
										 |  |  | 		return querySplitPos === -1 | 
					
						
							|  |  |  | 			? resource | 
					
						
							|  |  |  | 			: resource + maybeAbsolutePath.slice(querySplitPos); | 
					
						
							| 
									
										
										
										
											2018-12-08 01:12:04 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-12-23 20:22:07 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-26 17:40:42 +08:00
										 |  |  | 	if (WINDOWS_ABS_PATH_REGEXP.test(maybeAbsolutePath)) { | 
					
						
							|  |  |  | 		const querySplitPos = maybeAbsolutePath.indexOf("?"); | 
					
						
							|  |  |  | 		let resource = | 
					
						
							|  |  |  | 			querySplitPos === -1 | 
					
						
							|  |  |  | 				? maybeAbsolutePath | 
					
						
							|  |  |  | 				: maybeAbsolutePath.slice(0, querySplitPos); | 
					
						
							|  |  |  | 		resource = path.win32.relative(context, resource); | 
					
						
							|  |  |  | 		if (!WINDOWS_ABS_PATH_REGEXP.test(resource)) { | 
					
						
							| 
									
										
										
										
											2021-08-19 20:01:44 +08:00
										 |  |  | 			resource = relativePathToRequest( | 
					
						
							|  |  |  | 				resource.replace(WINDOWS_PATH_SEPARATOR_REGEXP, "/") | 
					
						
							|  |  |  | 			); | 
					
						
							| 
									
										
										
										
											2018-12-08 01:12:04 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-12-26 17:40:42 +08:00
										 |  |  | 		return querySplitPos === -1 | 
					
						
							|  |  |  | 			? resource | 
					
						
							|  |  |  | 			: resource + maybeAbsolutePath.slice(querySplitPos); | 
					
						
							| 
									
										
										
										
											2018-05-21 16:46:09 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-12-23 20:22:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// not an absolute path
 | 
					
						
							|  |  |  | 	return maybeAbsolutePath; | 
					
						
							| 
									
										
										
										
											2017-04-05 21:38:15 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @param {string} context context for relative path | 
					
						
							|  |  |  |  * @param {string} relativePath path | 
					
						
							|  |  |  |  * @returns {string} absolute path | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const requestToAbsolute = (context, relativePath) => { | 
					
						
							|  |  |  | 	if (relativePath.startsWith("./") || relativePath.startsWith("../")) | 
					
						
							|  |  |  | 		return path.join(context, relativePath); | 
					
						
							|  |  |  | 	return relativePath; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-03 23:21:06 +08:00
										 |  |  | const makeCacheable = realFn => { | 
					
						
							|  |  |  | 	/** @type {WeakMap<object, Map<string, ParsedResource>>} */ | 
					
						
							|  |  |  | 	const cache = new WeakMap(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const getCache = associatedObjectForCache => { | 
					
						
							|  |  |  | 		const entry = cache.get(associatedObjectForCache); | 
					
						
							|  |  |  | 		if (entry !== undefined) return entry; | 
					
						
							|  |  |  | 		/** @type {Map<string, ParsedResource>} */ | 
					
						
							|  |  |  | 		const map = new Map(); | 
					
						
							|  |  |  | 		cache.set(associatedObjectForCache, map); | 
					
						
							|  |  |  | 		return map; | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} str the path with query and fragment | 
					
						
							|  |  |  | 	 * @param {Object=} associatedObjectForCache an object to which the cache will be attached | 
					
						
							|  |  |  | 	 * @returns {ParsedResource} parsed parts | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	const fn = (str, associatedObjectForCache) => { | 
					
						
							|  |  |  | 		if (!associatedObjectForCache) return realFn(str); | 
					
						
							|  |  |  | 		const cache = getCache(associatedObjectForCache); | 
					
						
							|  |  |  | 		const entry = cache.get(str); | 
					
						
							|  |  |  | 		if (entry !== undefined) return entry; | 
					
						
							|  |  |  | 		const result = realFn(str); | 
					
						
							|  |  |  | 		cache.set(str, result); | 
					
						
							|  |  |  | 		return result; | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fn.bindCache = associatedObjectForCache => { | 
					
						
							|  |  |  | 		const cache = getCache(associatedObjectForCache); | 
					
						
							|  |  |  | 		return str => { | 
					
						
							|  |  |  | 			const entry = cache.get(str); | 
					
						
							|  |  |  | 			if (entry !== undefined) return entry; | 
					
						
							|  |  |  | 			const result = realFn(str); | 
					
						
							|  |  |  | 			cache.set(str, result); | 
					
						
							|  |  |  | 			return result; | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return fn; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const makeCacheableWithContext = fn => { | 
					
						
							| 
									
										
										
										
											2020-01-15 06:14:47 +08:00
										 |  |  | 	/** @type {WeakMap<object, Map<string, Map<string, string>>>} */ | 
					
						
							| 
									
										
										
										
											2019-01-19 19:40:00 +08:00
										 |  |  | 	const cache = new WeakMap(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} context context used to create relative path | 
					
						
							|  |  |  | 	 * @param {string} identifier identifier used to create relative path | 
					
						
							|  |  |  | 	 * @param {Object=} associatedObjectForCache an object to which the cache will be attached | 
					
						
							|  |  |  | 	 * @returns {string} the returned relative path | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	const cachedFn = (context, identifier, associatedObjectForCache) => { | 
					
						
							|  |  |  | 		if (!associatedObjectForCache) return fn(context, identifier); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		let innerCache = cache.get(associatedObjectForCache); | 
					
						
							|  |  |  | 		if (innerCache === undefined) { | 
					
						
							|  |  |  | 			innerCache = new Map(); | 
					
						
							|  |  |  | 			cache.set(associatedObjectForCache, innerCache); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		let cachedResult; | 
					
						
							|  |  |  | 		let innerSubCache = innerCache.get(context); | 
					
						
							|  |  |  | 		if (innerSubCache === undefined) { | 
					
						
							|  |  |  | 			innerCache.set(context, (innerSubCache = new Map())); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			cachedResult = innerSubCache.get(identifier); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (cachedResult !== undefined) { | 
					
						
							|  |  |  | 			return cachedResult; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			const result = fn(context, identifier); | 
					
						
							|  |  |  | 			innerSubCache.set(identifier, result); | 
					
						
							|  |  |  | 			return result; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-15 06:14:47 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Object=} associatedObjectForCache an object to which the cache will be attached | 
					
						
							|  |  |  | 	 * @returns {function(string, string): string} cached function | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	cachedFn.bindCache = associatedObjectForCache => { | 
					
						
							|  |  |  | 		let innerCache; | 
					
						
							|  |  |  | 		if (associatedObjectForCache) { | 
					
						
							|  |  |  | 			innerCache = cache.get(associatedObjectForCache); | 
					
						
							|  |  |  | 			if (innerCache === undefined) { | 
					
						
							|  |  |  | 				innerCache = new Map(); | 
					
						
							|  |  |  | 				cache.set(associatedObjectForCache, innerCache); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			innerCache = new Map(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/** | 
					
						
							|  |  |  | 		 * @param {string} context context used to create relative path | 
					
						
							|  |  |  | 		 * @param {string} identifier identifier used to create relative path | 
					
						
							|  |  |  | 		 * @returns {string} the returned relative path | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		const boundFn = (context, identifier) => { | 
					
						
							|  |  |  | 			let cachedResult; | 
					
						
							|  |  |  | 			let innerSubCache = innerCache.get(context); | 
					
						
							|  |  |  | 			if (innerSubCache === undefined) { | 
					
						
							|  |  |  | 				innerCache.set(context, (innerSubCache = new Map())); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				cachedResult = innerSubCache.get(identifier); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (cachedResult !== undefined) { | 
					
						
							|  |  |  | 				return cachedResult; | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				const result = fn(context, identifier); | 
					
						
							|  |  |  | 				innerSubCache.set(identifier, result); | 
					
						
							|  |  |  | 				return result; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return boundFn; | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} context context used to create relative path | 
					
						
							|  |  |  | 	 * @param {Object=} associatedObjectForCache an object to which the cache will be attached | 
					
						
							|  |  |  | 	 * @returns {function(string): string} cached function | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	cachedFn.bindContextCache = (context, associatedObjectForCache) => { | 
					
						
							|  |  |  | 		let innerSubCache; | 
					
						
							|  |  |  | 		if (associatedObjectForCache) { | 
					
						
							|  |  |  | 			let innerCache = cache.get(associatedObjectForCache); | 
					
						
							|  |  |  | 			if (innerCache === undefined) { | 
					
						
							|  |  |  | 				innerCache = new Map(); | 
					
						
							|  |  |  | 				cache.set(associatedObjectForCache, innerCache); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			innerSubCache = innerCache.get(context); | 
					
						
							|  |  |  | 			if (innerSubCache === undefined) { | 
					
						
							|  |  |  | 				innerCache.set(context, (innerSubCache = new Map())); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			innerSubCache = new Map(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/** | 
					
						
							|  |  |  | 		 * @param {string} identifier identifier used to create relative path | 
					
						
							|  |  |  | 		 * @returns {string} the returned relative path | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		const boundFn = identifier => { | 
					
						
							|  |  |  | 			const cachedResult = innerSubCache.get(identifier); | 
					
						
							|  |  |  | 			if (cachedResult !== undefined) { | 
					
						
							|  |  |  | 				return cachedResult; | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				const result = fn(context, identifier); | 
					
						
							|  |  |  | 				innerSubCache.set(identifier, result); | 
					
						
							|  |  |  | 				return result; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return boundFn; | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-19 19:40:00 +08:00
										 |  |  | 	return cachedFn; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-15 05:31:11 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param {string} context context for relative path | 
					
						
							|  |  |  |  * @param {string} identifier identifier for path | 
					
						
							| 
									
										
										
										
											2018-05-08 20:31:51 +08:00
										 |  |  |  * @returns {string} a converted relative path | 
					
						
							| 
									
										
										
										
											2018-04-15 05:31:11 +08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2017-07-18 04:30:29 +08:00
										 |  |  | const _makePathsRelative = (context, identifier) => { | 
					
						
							|  |  |  | 	return identifier | 
					
						
							| 
									
										
										
										
											2020-03-13 00:51:26 +08:00
										 |  |  | 		.split(SEGMENTS_SPLIT_REGEXP) | 
					
						
							| 
									
										
										
										
											2018-12-08 01:12:04 +08:00
										 |  |  | 		.map(str => absoluteToRequest(context, str)) | 
					
						
							| 
									
										
										
										
											2017-07-18 04:30:29 +08:00
										 |  |  | 		.join(""); | 
					
						
							| 
									
										
										
										
											2017-07-18 04:47:18 +08:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2017-07-12 07:19:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-03 23:21:06 +08:00
										 |  |  | exports.makePathsRelative = makeCacheableWithContext(_makePathsRelative); | 
					
						
							| 
									
										
										
										
											2018-07-04 15:59:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-19 20:01:44 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param {string} context context for relative path | 
					
						
							|  |  |  |  * @param {string} identifier identifier for path | 
					
						
							|  |  |  |  * @returns {string} a converted relative path | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const _makePathsAbsolute = (context, identifier) => { | 
					
						
							|  |  |  | 	return identifier | 
					
						
							|  |  |  | 		.split(SEGMENTS_SPLIT_REGEXP) | 
					
						
							|  |  |  | 		.map(str => requestToAbsolute(context, str)) | 
					
						
							|  |  |  | 		.join(""); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-03 23:21:06 +08:00
										 |  |  | exports.makePathsAbsolute = makeCacheableWithContext(_makePathsAbsolute); | 
					
						
							| 
									
										
										
										
											2021-08-19 20:01:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-04 15:59:22 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @param {string} context absolute context path | 
					
						
							|  |  |  |  * @param {string} request any request string may containing absolute paths, query string, etc. | 
					
						
							|  |  |  |  * @returns {string} a new request string avoiding absolute paths when possible | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-01-19 19:40:00 +08:00
										 |  |  | const _contextify = (context, request) => { | 
					
						
							| 
									
										
										
										
											2018-07-04 15:59:22 +08:00
										 |  |  | 	return request | 
					
						
							|  |  |  | 		.split("!") | 
					
						
							| 
									
										
										
										
											2018-12-08 01:12:04 +08:00
										 |  |  | 		.map(r => absoluteToRequest(context, r)) | 
					
						
							| 
									
										
										
										
											2018-07-04 15:59:22 +08:00
										 |  |  | 		.join("!"); | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2019-01-19 19:40:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-03 23:21:06 +08:00
										 |  |  | const contextify = makeCacheableWithContext(_contextify); | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | exports.contextify = contextify; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @param {string} context absolute context path | 
					
						
							|  |  |  |  * @param {string} request any request string | 
					
						
							|  |  |  |  * @returns {string} a new request string using absolute paths when possible | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const _absolutify = (context, request) => { | 
					
						
							|  |  |  | 	return request | 
					
						
							|  |  |  | 		.split("!") | 
					
						
							|  |  |  | 		.map(r => requestToAbsolute(context, r)) | 
					
						
							|  |  |  | 		.join("!"); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-03 23:21:06 +08:00
										 |  |  | const absolutify = makeCacheableWithContext(_absolutify); | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | exports.absolutify = absolutify; | 
					
						
							| 
									
										
										
										
											2020-07-03 20:45:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-11 15:31:46 +08:00
										 |  |  | const PATH_QUERY_FRAGMENT_REGEXP = | 
					
						
							|  |  |  | 	/^((?:\0.|[^?#\0])*)(\?(?:\0.|[^#\0])*)?(#.*)?$/; | 
					
						
							| 
									
										
										
										
											2022-02-04 23:12:20 +08:00
										 |  |  | const PATH_QUERY_REGEXP = /^((?:\0.|[^?\0])*)(\?.*)?$/; | 
					
						
							| 
									
										
										
										
											2020-07-03 20:45:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-06 23:39:52 +08:00
										 |  |  | /** @typedef {{ resource: string, path: string, query: string, fragment: string }} ParsedResource */ | 
					
						
							| 
									
										
										
										
											2022-02-03 23:21:06 +08:00
										 |  |  | /** @typedef {{ resource: string, path: string, query: string }} ParsedResourceWithoutFragment */ | 
					
						
							| 
									
										
										
										
											2020-07-06 23:39:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-03 20:45:49 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @param {string} str the path with query and fragment | 
					
						
							| 
									
										
										
										
											2020-07-06 23:39:52 +08:00
										 |  |  |  * @returns {ParsedResource} parsed parts | 
					
						
							| 
									
										
										
										
											2020-07-03 20:45:49 +08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2020-07-06 23:39:52 +08:00
										 |  |  | const _parseResource = str => { | 
					
						
							| 
									
										
										
										
											2020-07-03 20:45:49 +08:00
										 |  |  | 	const match = PATH_QUERY_FRAGMENT_REGEXP.exec(str); | 
					
						
							|  |  |  | 	return { | 
					
						
							| 
									
										
										
										
											2020-07-06 23:39:52 +08:00
										 |  |  | 		resource: str, | 
					
						
							| 
									
										
										
										
											2020-09-30 15:51:16 +08:00
										 |  |  | 		path: match[1].replace(/\0(.)/g, "$1"), | 
					
						
							|  |  |  | 		query: match[2] ? match[2].replace(/\0(.)/g, "$1") : "", | 
					
						
							| 
									
										
										
										
											2020-07-03 20:45:49 +08:00
										 |  |  | 		fragment: match[3] || "" | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2022-02-03 23:21:06 +08:00
										 |  |  | exports.parseResource = makeCacheable(_parseResource); | 
					
						
							| 
									
										
										
										
											2020-07-06 23:39:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-03 23:21:06 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Parse resource, skips fragment part | 
					
						
							|  |  |  |  * @param {string} str the path with query and fragment | 
					
						
							|  |  |  |  * @returns {ParsedResourceWithoutFragment} parsed parts | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const _parseResourceWithoutFragment = str => { | 
					
						
							|  |  |  | 	const match = PATH_QUERY_REGEXP.exec(str); | 
					
						
							|  |  |  | 	return { | 
					
						
							|  |  |  | 		resource: str, | 
					
						
							|  |  |  | 		path: match[1].replace(/\0(.)/g, "$1"), | 
					
						
							|  |  |  | 		query: match[2] ? match[2].replace(/\0(.)/g, "$1") : "" | 
					
						
							| 
									
										
										
										
											2020-07-06 23:39:52 +08:00
										 |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2022-02-03 23:21:06 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | exports.parseResourceWithoutFragment = makeCacheable( | 
					
						
							|  |  |  | 	_parseResourceWithoutFragment | 
					
						
							|  |  |  | ); | 
					
						
							| 
									
										
										
										
											2020-08-04 04:55:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @param {string} filename the filename which should be undone | 
					
						
							| 
									
										
										
										
											2021-03-16 20:07:45 +08:00
										 |  |  |  * @param {string} outputPath the output path that is restored (only relevant when filename contains "..") | 
					
						
							| 
									
										
										
										
											2020-08-04 04:55:51 +08:00
										 |  |  |  * @param {boolean} enforceRelative true returns ./ for empty paths | 
					
						
							| 
									
										
										
										
											2021-03-16 20:07:45 +08:00
										 |  |  |  * @returns {string} repeated ../ to leave the directory of the provided filename to be back on output dir | 
					
						
							| 
									
										
										
										
											2020-08-04 04:55:51 +08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-03-16 20:07:45 +08:00
										 |  |  | exports.getUndoPath = (filename, outputPath, enforceRelative) => { | 
					
						
							| 
									
										
										
										
											2020-08-04 04:55:51 +08:00
										 |  |  | 	let depth = -1; | 
					
						
							| 
									
										
										
										
											2021-03-16 20:07:45 +08:00
										 |  |  | 	let append = ""; | 
					
						
							|  |  |  | 	outputPath = outputPath.replace(/[\\/]$/, ""); | 
					
						
							| 
									
										
										
										
											2020-08-04 04:55:51 +08:00
										 |  |  | 	for (const part of filename.split(/[/\\]+/)) { | 
					
						
							| 
									
										
										
										
											2021-03-16 20:07:45 +08:00
										 |  |  | 		if (part === "..") { | 
					
						
							|  |  |  | 			if (depth > -1) { | 
					
						
							|  |  |  | 				depth--; | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				const i = outputPath.lastIndexOf("/"); | 
					
						
							|  |  |  | 				const j = outputPath.lastIndexOf("\\"); | 
					
						
							|  |  |  | 				const pos = i < 0 ? j : j < 0 ? i : Math.max(i, j); | 
					
						
							|  |  |  | 				if (pos < 0) return outputPath + "/"; | 
					
						
							|  |  |  | 				append = outputPath.slice(pos + 1) + "/" + append; | 
					
						
							|  |  |  | 				outputPath = outputPath.slice(0, pos); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else if (part !== ".") { | 
					
						
							|  |  |  | 			depth++; | 
					
						
							| 
									
										
										
										
											2020-08-04 04:55:51 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-16 20:07:45 +08:00
										 |  |  | 	return depth > 0 | 
					
						
							|  |  |  | 		? `${"../".repeat(depth)}${append}` | 
					
						
							|  |  |  | 		: enforceRelative | 
					
						
							| 
									
										
										
										
											2024-01-14 09:41:34 +08:00
										 |  |  | 			? `./${append}` | 
					
						
							|  |  |  | 			: append; | 
					
						
							| 
									
										
										
										
											2020-08-04 04:55:51 +08:00
										 |  |  | }; |