simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

LinkResolver.js (3215B)


      1 /*
      2 	MIT License http://www.opensource.org/licenses/mit-license.php
      3 	Author Tobias Koppers @sokra
      4 */
      5 "use strict";
      6 
      7 const fs = require("fs");
      8 const path = require("path");
      9 
     10 // macOS, Linux, and Windows all rely on these errors
     11 const EXPECTED_ERRORS = new Set(["EINVAL", "ENOENT"]);
     12 
     13 // On Windows there is also this error in some cases
     14 if (process.platform === "win32") EXPECTED_ERRORS.add("UNKNOWN");
     15 
     16 class LinkResolver {
     17 	constructor() {
     18 		this.cache = new Map();
     19 	}
     20 
     21 	/**
     22 	 * @param {string} file path to file or directory
     23 	 * @returns {string[]} array of file and all symlinks contributed in the resolving process (first item is the resolved file)
     24 	 */
     25 	resolve(file) {
     26 		const cacheEntry = this.cache.get(file);
     27 		if (cacheEntry !== undefined) {
     28 			return cacheEntry;
     29 		}
     30 		const parent = path.dirname(file);
     31 		if (parent === file) {
     32 			// At root of filesystem there can't be a link
     33 			const result = Object.freeze([file]);
     34 			this.cache.set(file, result);
     35 			return result;
     36 		}
     37 		// resolve the parent directory to find links there and get the real path
     38 		const parentResolved = this.resolve(parent);
     39 		let realFile = file;
     40 
     41 		// is the parent directory really somewhere else?
     42 		if (parentResolved[0] !== parent) {
     43 			// get the real location of file
     44 			const basename = path.basename(file);
     45 			realFile = path.resolve(parentResolved[0], basename);
     46 		}
     47 		// try to read the link content
     48 		try {
     49 			const linkContent = fs.readlinkSync(realFile);
     50 
     51 			// resolve the link content relative to the parent directory
     52 			const resolvedLink = path.resolve(parentResolved[0], linkContent);
     53 
     54 			// recursive resolve the link content for more links in the structure
     55 			const linkResolved = this.resolve(resolvedLink);
     56 
     57 			// merge parent and link resolve results
     58 			let result;
     59 			if (linkResolved.length > 1 && parentResolved.length > 1) {
     60 				// when both contain links we need to duplicate them with a Set
     61 				const resultSet = new Set(linkResolved);
     62 				// add the link
     63 				resultSet.add(realFile);
     64 				// add all symlinks of the parent
     65 				for (let i = 1; i < parentResolved.length; i++) {
     66 					resultSet.add(parentResolved[i]);
     67 				}
     68 				result = Object.freeze(Array.from(resultSet));
     69 			} else if (parentResolved.length > 1) {
     70 				// we have links in the parent but not for the link content location
     71 				result = parentResolved.slice();
     72 				result[0] = linkResolved[0];
     73 				// add the link
     74 				result.push(realFile);
     75 				Object.freeze(result);
     76 			} else if (linkResolved.length > 1) {
     77 				// we can return the link content location result
     78 				result = linkResolved.slice();
     79 				// add the link
     80 				result.push(realFile);
     81 				Object.freeze(result);
     82 			} else {
     83 				// neither link content location nor parent have links
     84 				// this link is the only link here
     85 				result = Object.freeze([
     86 					// the resolve real location
     87 					linkResolved[0],
     88 					// add the link
     89 					realFile
     90 				]);
     91 			}
     92 			this.cache.set(file, result);
     93 			return result;
     94 		} catch (e) {
     95 			if (!EXPECTED_ERRORS.has(e.code)) {
     96 				throw e;
     97 			}
     98 			// no link
     99 			const result = parentResolved.slice();
    100 			result[0] = realFile;
    101 			Object.freeze(result);
    102 			this.cache.set(file, result);
    103 			return result;
    104 		}
    105 	}
    106 }
    107 module.exports = LinkResolver;