'use strict' const path = require('path') const fs = require('graceful-fs') const pathExists = require('../path-exists').pathExists /** * Function that returns two types of paths, one relative to symlink, and one * relative to the current working directory. Checks if path is absolute or * relative. If the path is relative, this function checks if the path is * relative to symlink or relative to current working directory. This is an * initiative to find a smarter `srcpath` to supply when building symlinks. * This allows you to determine which path to use out of one of three possible * types of source paths. The first is an absolute path. This is detected by * `path.isAbsolute()`. When an absolute path is provided, it is checked to * see if it exists. If it does it's used, if not an error is returned * (callback)/ thrown (sync). The other two options for `srcpath` are a * relative url. By default Node's `fs.symlink` works by creating a symlink * using `dstpath` and expects the `srcpath` to be relative to the newly * created symlink. If you provide a `srcpath` that does not exist on the file * system it results in a broken symlink. To minimize this, the function * checks to see if the 'relative to symlink' source file exists, and if it * does it will use it. If it does not, it checks if there's a file that * exists that is relative to the current working directory, if does its used. * This preserves the expectations of the original fs.symlink spec and adds * the ability to pass in `relative to current working direcotry` paths. */ function symlinkPaths (srcpath, dstpath, callback) { if (path.isAbsolute(srcpath)) { return fs.lstat(srcpath, (err) => { if (err) { err.message = err.message.replace('lstat', 'ensureSymlink') return callback(err) } return callback(null, { 'toCwd': srcpath, 'toDst': srcpath }) }) } else { const dstdir = path.dirname(dstpath) const relativeToDst = path.join(dstdir, srcpath) return pathExists(relativeToDst, (err, exists) => { if (err) return callback(err) if (exists) { return callback(null, { 'toCwd': relativeToDst, 'toDst': srcpath }) } else { return fs.lstat(srcpath, (err) => { if (err) { err.message = err.message.replace('lstat', 'ensureSymlink') return callback(err) } return callback(null, { 'toCwd': srcpath, 'toDst': path.relative(dstdir, srcpath) }) }) } }) } } function symlinkPathsSync (srcpath, dstpath) { let exists if (path.isAbsolute(srcpath)) { exists = fs.existsSync(srcpath) if (!exists) throw new Error('absolute srcpath does not exist') return { 'toCwd': srcpath, 'toDst': srcpath } } else { const dstdir = path.dirname(dstpath) const relativeToDst = path.join(dstdir, srcpath) exists = fs.existsSync(relativeToDst) if (exists) { return { 'toCwd': relativeToDst, 'toDst': srcpath } } else { exists = fs.existsSync(srcpath) if (!exists) throw new Error('relative srcpath does not exist') return { 'toCwd': srcpath, 'toDst': path.relative(dstdir, srcpath) } } } } module.exports = { symlinkPaths, symlinkPathsSync }