Create relative symlinks using absolute paths in Node.JS
Asked Answered
L

2

19

I have a projects with the following structure:

project-root
├── some-dir
│   ├── alice.json
│   ├── bob.json
│   └── dave.json
└── ...

I want to create symlinks like the following ones:

  • foo -> alice.json

I chose to use the fs.symlink function:

fs.symlink(srcpath, dstpath[, type], callback)

Asynchronous symlink(2). No arguments other than a possible exception are given to the completion callback. The type argument can be set to 'dir', 'file', or 'junction' (default is 'file') and is only available on Windows (ignored on other platforms). Note that Windows junction points require the destination path to be absolute. When using 'junction', the destination argument will automatically be normalized to absolute path.

So, I did:

require("fs").symlink(
  projectRoot + "/some-dir/alice.json"
, projectRoot + "/some-dir/foo"
, function (err) { console.log(err || "Done."); }
);

This creates the foo symlink. However, since the paths are absolute the symlink uses absolute path as well.

How can I make the symlink path relative to a directory (in this case to some-dir)?

This will prevent errors when the parent directories are renamed or the project is moved on another machine.

The dirty alternative I see is using exec("ln -s alice.json foo", { cwd: pathToSomeDir }, callback);, but I would like to avoid that and use the NodeJS API.

So, how can I make relative symlinks using absolute paths in NodeJS?

Landreth answered 21/4, 2015 at 16:3 Comment(0)
O
18

Option 1: Use process.chdir() to change the current working directory of the process to projectRoot. Then, provide relative paths to fs.symlink().

Option 2: Use path.relative() or otherwise generate the relative path between your symlink and its target. Pass that relative path as the first argument to fs.symlink() while providing an absolute path for the second argument. For example:

var relativePath = path.relative('/some-dir', '/some-dir/alice.json');
fs.symlink(relativePath, '/some-dir/foo', callback);
Opuscule answered 21/4, 2015 at 16:8 Comment(10)
Might this be a situation where you would want the entire process to have projectRoot as the working directory anyway?Opuscule
Yes and no - it's fine when using it as command line tool, but also, this tool I develop can be used as library. That's why I don't like setting the current working directory of the process.Elfin
OK, the alternative is to use path.relative(). Will edit my answer.Opuscule
The second solution looks better. Could you please add an example as well?Elfin
Whoops, I had a small error in the example code. Just updated it.Opuscule
Let us continue this discussion in chat.Elfin
@Trott: Could you please edit Option2 to this correct solution?: fs.symlink(source, path.join(dir, target), 'file', function (err) {...});, where source = 'alice.txt', target = 'alice_symlink.txt', dir = './project-root/some-dir'. Thank you.Antakiya
Why? What does that answer provide that is missing or incorrect in the current answer?Opuscule
I'm writing a CLI app and not sure my path back to the source which is inside the process dir. It will be run from various places, am looking at option 1. Perhaps I will store the current CWD and then restore it by changing back to users CWD.... sound OK?Roselani
Using process.chdir(), especially inside a general use module, seems like something that could cause unintended consequences, and thus probably shouldn't be used if it doesn't have to be.... might even be a good thing to make sure doesn't exist inside a codebase, as a general guideline...... unless it is used in a "top level" module or in some sort of isolated process that you don't intend to allow other processes to alter the node.js sequence of.Amil
A
4
const path = require('path');
const fs = require('fs');

// The actual symlink entity in the file system
const source = /* absolute path of source */;

// Where the symlink should point to
const absolute_target = /* absolute path of target */;

const target = path.relative(
    path.dirname(source),
    absolute_target
);

fs.symlink(
    target,   
    source,
    (err) => {

    }
);
Amil answered 31/7, 2019 at 1:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.