You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			108 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			JavaScript
		
	
		
		
			
		
	
	
			108 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			JavaScript
		
	
| 
											9 months ago
										 | /* | ||
|  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | ||
|  | 	Author Tobias Koppers @sokra | ||
|  | */ | ||
|  | "use strict"; | ||
|  | 
 | ||
|  | const fs = require("fs"); | ||
|  | const path = require("path"); | ||
|  | 
 | ||
|  | // macOS, Linux, and Windows all rely on these errors
 | ||
|  | const EXPECTED_ERRORS = new Set(["EINVAL", "ENOENT"]); | ||
|  | 
 | ||
|  | // On Windows there is also this error in some cases
 | ||
|  | if (process.platform === "win32") EXPECTED_ERRORS.add("UNKNOWN"); | ||
|  | 
 | ||
|  | class LinkResolver { | ||
|  | 	constructor() { | ||
|  | 		this.cache = new Map(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {string} file path to file or directory | ||
|  | 	 * @returns {string[]} array of file and all symlinks contributed in the resolving process (first item is the resolved file) | ||
|  | 	 */ | ||
|  | 	resolve(file) { | ||
|  | 		const cacheEntry = this.cache.get(file); | ||
|  | 		if (cacheEntry !== undefined) { | ||
|  | 			return cacheEntry; | ||
|  | 		} | ||
|  | 		const parent = path.dirname(file); | ||
|  | 		if (parent === file) { | ||
|  | 			// At root of filesystem there can't be a link
 | ||
|  | 			const result = Object.freeze([file]); | ||
|  | 			this.cache.set(file, result); | ||
|  | 			return result; | ||
|  | 		} | ||
|  | 		// resolve the parent directory to find links there and get the real path
 | ||
|  | 		const parentResolved = this.resolve(parent); | ||
|  | 		let realFile = file; | ||
|  | 
 | ||
|  | 		// is the parent directory really somewhere else?
 | ||
|  | 		if (parentResolved[0] !== parent) { | ||
|  | 			// get the real location of file
 | ||
|  | 			const basename = path.basename(file); | ||
|  | 			realFile = path.resolve(parentResolved[0], basename); | ||
|  | 		} | ||
|  | 		// try to read the link content
 | ||
|  | 		try { | ||
|  | 			const linkContent = fs.readlinkSync(realFile); | ||
|  | 
 | ||
|  | 			// resolve the link content relative to the parent directory
 | ||
|  | 			const resolvedLink = path.resolve(parentResolved[0], linkContent); | ||
|  | 
 | ||
|  | 			// recursive resolve the link content for more links in the structure
 | ||
|  | 			const linkResolved = this.resolve(resolvedLink); | ||
|  | 
 | ||
|  | 			// merge parent and link resolve results
 | ||
|  | 			let result; | ||
|  | 			if (linkResolved.length > 1 && parentResolved.length > 1) { | ||
|  | 				// when both contain links we need to duplicate them with a Set
 | ||
|  | 				const resultSet = new Set(linkResolved); | ||
|  | 				// add the link
 | ||
|  | 				resultSet.add(realFile); | ||
|  | 				// add all symlinks of the parent
 | ||
|  | 				for (let i = 1; i < parentResolved.length; i++) { | ||
|  | 					resultSet.add(parentResolved[i]); | ||
|  | 				} | ||
|  | 				result = Object.freeze(Array.from(resultSet)); | ||
|  | 			} else if (parentResolved.length > 1) { | ||
|  | 				// we have links in the parent but not for the link content location
 | ||
|  | 				result = parentResolved.slice(); | ||
|  | 				result[0] = linkResolved[0]; | ||
|  | 				// add the link
 | ||
|  | 				result.push(realFile); | ||
|  | 				Object.freeze(result); | ||
|  | 			} else if (linkResolved.length > 1) { | ||
|  | 				// we can return the link content location result
 | ||
|  | 				result = linkResolved.slice(); | ||
|  | 				// add the link
 | ||
|  | 				result.push(realFile); | ||
|  | 				Object.freeze(result); | ||
|  | 			} else { | ||
|  | 				// neither link content location nor parent have links
 | ||
|  | 				// this link is the only link here
 | ||
|  | 				result = Object.freeze([ | ||
|  | 					// the resolve real location
 | ||
|  | 					linkResolved[0], | ||
|  | 					// add the link
 | ||
|  | 					realFile | ||
|  | 				]); | ||
|  | 			} | ||
|  | 			this.cache.set(file, result); | ||
|  | 			return result; | ||
|  | 		} catch (e) { | ||
|  | 			if (!EXPECTED_ERRORS.has(e.code)) { | ||
|  | 				throw e; | ||
|  | 			} | ||
|  | 			// no link
 | ||
|  | 			const result = parentResolved.slice(); | ||
|  | 			result[0] = realFile; | ||
|  | 			Object.freeze(result); | ||
|  | 			this.cache.set(file, result); | ||
|  | 			return result; | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | module.exports = LinkResolver; |