Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.nvmrc for projects #110

Closed
westoque opened this issue Apr 17, 2012 · 107 comments
Closed

.nvmrc for projects #110

westoque opened this issue Apr 17, 2012 · 107 comments
Labels
feature requests I want a new feature in nvm!

Comments

@westoque
Copy link

It would be nice if we had a ".nvmrc" file for a project, kind of like ".rvmrc" for ruby. I'll start prototyping this later. What do you guys think?

@creationix
Copy link
Collaborator

I'm not sure what it would do. NVM doesn't wrap the node executable, it just helps you manage multiple $PATH values.

@booo
Copy link

booo commented Apr 17, 2012

Switch to the node version specified in .nvmrc? Why not use the package.json file?

@creationix
Copy link
Collaborator

But what will do the switching? Are you going to run nvm readconfig or something every time you enter a new folder?

@booo
Copy link

booo commented Apr 17, 2012

As far as I know the rvm checks on every cd if the new folder contains a rvmrc and loads the appropriate config/ruby version. Maybe a ruby expert should say something about this :)

http://beginrescueend.com/workflow/rvmrc/

@westoque
Copy link
Author

I just looked at how rvm implemented this. I am going to do the same approach.

@slaskis
Copy link

slaskis commented Apr 20, 2012

I like this idea too, except that using the package.json instead of a separate .npmrc makes a a lot of sense to me as @booo says

@chakrit
Copy link
Contributor

chakrit commented Jul 3, 2012

There is already an engine version specified in the package.json file. There is zero need to add yet another dotfile to do this.

I'd love for nvm to do this automatically as well. But definitely not by adding yet another .whateverc.

@abe33
Copy link

abe33 commented Sep 24, 2012

Hi guys,

For those interested, I spent some time this week end to write a proof of concept around the idea of parsing the package.json on a cd. Actually I use a ruby script to parse the json file and define what to do, since my bash-jutsu is quite limited. I didn't work much on the version operators either, so there's only a limited support (>=, <= and = are supported actually).

The fork can be found here:
https://github.com/abe33/nvm/tree/feature_automatic_use_on_cd_with_package

@chakrit
Copy link
Contributor

chakrit commented Sep 24, 2012

Idea: regarding "auto-switching", would it be better if we just add an nvm command to pick the best version that's suited to the current folder's package.json?

something like

nvm select # auto-select a local version that best matches what's specified in package.json

I have used rvm before and honestly didn't like the cd-stealing approach. I already have a few commands that try to steal my cd (autojump and rvm for example) and does some extra processings that I'm not aware of. IMO, running anything automatically on a cd is a bad approach.

@abe33
Copy link

abe33 commented Sep 29, 2012

@chakrit : The last version includes the nvm select command as you requested and dissociate the change directory hook in a separate file. I also added the support for all the version range expressions (as documented here: https://npmjs.org/doc/json.html#dependencies, minus the git and http ones). I guess there's some edge cases I didn't tested yet, I'll update it as soon as I'll find them.

@chakrit
Copy link
Contributor

chakrit commented Sep 29, 2012

@abe33 nice! :) using your fork right now.

EDIT: ruby? ಠ__ಠ

@abe33
Copy link

abe33 commented Sep 29, 2012

@chakrit : Yes, as I said in my first comment, my skills with shell script aren't that good, and I had hard times to find out how to parse a json file with pure shell - re-writing a json parser wasn't really part of the plan ^^ - and writing the version matching algorithm was beyond my knowledge by far.
If people have advice to how implement this in shell, I'll gladly try to port the ruby code :).

@tiye
Copy link

tiye commented Apr 14, 2013

Related feature requst: http://cnodejs.org/topic/516a1f0e6d382773062dfd8a
I think use packge.json for auto-switching is a nice solution.

@ghost
Copy link

ghost commented Apr 16, 2013

I also think it would be nice to add an nvm command that can try to figure out the engine to use by parsing the package.json. I have a lot of projects with different node versions, so it’d be a time saver if I don’t have to go look into package.json myself to figure out which version I want to tell nvm to use.

@ghost
Copy link

ghost commented Apr 16, 2013

But yeah, I’m not sure how easy it is to parse package.json in pure Bash.

@creationix
Copy link
Collaborator

@Hydrozen sounds like you need a new tool. Let's not bloat nvm with complex features like this. Searching recursively through package.json files is non-trivial for a bash function. (parsing a single json file in bash is hard enough) Write a new tool that works in any version of node that outputs a string for the ideal node version to run, and then pass that string to nvm.

@creationix
Copy link
Collaborator

I'm going to close this issue. After thinking about this for a while, it's outside the scope of nvm. Nvm is used to install and switch between versions of node manually. There is an automatic default version, but I really want to keep the tool simple.

I do think it would be a great project for someone to create a new tool written in node.js that looks at the package.json files in the current working tree and outputs the ideal node version to run. This tool will need to be installed somewhere outside nvm's tree and in your $PATH so that it will stay in scope as you switch between node versions. It can use #!/bin/env node in it's shebang. JSON parsing and file reading API's haven't changed in node in a long time, so it should run in nearly any version you want to use.

@abe33
Copy link

abe33 commented Apr 17, 2013

@creationix No pb for me, the ruby code can easily be ported to js so I'll give it a try when I can find some spare time.

@ziyan-junaideen
Copy link

It will be a cool thing to have!

@potomak
Copy link

potomak commented Jun 3, 2013

I would pay 2$ if somebody implements this feature.

@creationix
Copy link
Collaborator

@potomak you'll pay someone to write a patch I've said I won't accept, or pay someone to write a separate utility as I suggested? (If it's the first, you'll just have to maintain a fork to keep the patch. there won't be hurt feelings)

@potomak
Copy link

potomak commented Jun 3, 2013

@creationix the best would be to get this patch merged inside your repo (note I'd give part of that bounty to you as the maintainer of the project) but I could pay also for the second solution.

@chakrit
Copy link
Contributor

chakrit commented Jun 3, 2013

@creationix plugin nag : )

@koenpunt
Copy link
Contributor

koenpunt commented Jun 3, 2013

The .nvmrc is already read when calling nvm use, so what is the point of this issue? Or is it me being ignorant right now?

@creationix
Copy link
Collaborator

@koenpunt right, I was talking about parsing package.json in nvm. That's outside the scope of the project.

@ghost
Copy link

ghost commented Jun 3, 2013

I didn’t even know about the .nvmrc file. That’s good enough for me.

@cdnbacon
Copy link

@creationix Have you considered something like this?

nvm use $(node -e 'console.log(require("./package.json").engines.node)')

@koenpunt
Copy link
Contributor

That doesn't work if the engine is defined as >=0.4.0..

@cdnbacon
Copy link

cdnbacon commented Aug 1, 2013

@koenpunt , depending on the semantics you desire, you can use something like this if you want to use the least-recent supported version in package.json

nvm use $(node -e 'console.log(require("./package.json").engines.node.replace(/[^\d\.]+/g, ""))')

@ljharb
Copy link
Member

ljharb commented Mar 8, 2016

@wbyoung want to PR a link to it in the readme under that section?

@Vadorequest
Copy link

Hello,
I just tried @ohcibi 's solution and added the following to my .zshrc

# Auto change the nvm version based on a .nvmrc file based on the current directory. See https://github.com/creationix/nvm/issues/110#issuecomment-190125863
autoload -U add-zsh-hook
load-nvmrc() {
  if [[ -f .nvmrc && -r .nvmrc ]]; then
    nvm use
  fi
}
add-zsh-hook chpwd load-nvmrc

I've created a .nvmrc file in two projects, one using 0.12.12 and the other using 5.5.0.

When I open both projects using WebStorm IDE and run a nvm current in the internal console (using zsh) I see the proper version displayed. (meaning it works great, or at least that's what it looks like).

But when I manually cd into one project to another using a proper zsh shell (not from the IDE) it displays the same version. Did I miss something? I thought using cd would automatically run nvm use if a nvmrc file exists.

And I'm wondering what happens if I have two shells open at the same time in different project, showing a different version. Is nvm able to use one version by directory and somehow manage to use several different versions at the same time? Or is my shell lying and actually using the latest nvm version loaded? Thanks for input.

@ohcibi
Copy link
Contributor

ohcibi commented Mar 9, 2016

@Vadorequest the hook should in fact be executed whenever you cd into a directory. There might be something wrong with your environment but this is out of scope (as stated in the readme) please post that question on stackoverflow or similar.

@ljharb close this issue?

@Vadorequest
Copy link

@ohcibi Okay, I just wanted to know if I missed a step or not.

What about that part?

And I'm wondering what happens if I have two shells open at the same time in different project, showing a different version. Is nvm able to use one version by directory and somehow manage to use several different versions at the same time? Or is my shell lying and actually using the latest nvm version loaded?

Thanks.

@ohcibi
Copy link
Contributor

ohcibi commented Mar 9, 2016

@Vadorequest each shell session has its own environment, but thats way out of scope of this project 8-).

@Vadorequest
Copy link

Oh, I wasn't aware of that. Thank you ;)
PS: It works fine, I just forgot to save the zshrc, sublime text... ;)

@scaryguy
Copy link

scaryguy commented Apr 4, 2016

I think this is a must-be feature for nvm.

Don't you ever work with multiple projects at the same time period, guys? Do you really love to check 'uhmm.. which node version I had used for this particular project..'? This process should be automated and .rvmrc is a very good example.

It's not a fact of simplicity not having this feature. If you would really like that kind of 'simplicity' you wouldn't use a tool like nvm and you would just keep changing the paths manually.

I vote +1 for reconsideration.

@ljharb
Copy link
Member

ljharb commented Apr 4, 2016

@scaryguy we have had .nvmrc for a very long time (since April 2014), so no reconsideration is needed. The only remaining thing being discussed on this thread is automatically hooking into cd to run nvm use for you.

@pulkitsinghal
Copy link

pulkitsinghal commented Aug 12, 2016

My solution isn't a solution generic enough but I mixed together the previous suggestions with stackoverflow to add the following for mac in my ~/.bash_profile and it worked well:

# change title name of tab in terminal
function title {
    echo -ne "\033]0;"$*"\007"
}

cd() {
  builtin cd "$@" || return
  #echo $PREV_PWD
  if [ "$PWD" != "$PREV_PWD" ]; then
    PREV_PWD="$PWD";
    title $(echo ${PWD##*/}) $(node -v);
    if [ -e ".nvmrc" ]; then
      nvm use;
      # set tab terminal name to be cwd and node version
      title $(echo ${PWD##*/}) $(node -v);
    else
      nvm use default;
    fi
  fi
}

Now I can do:

$ cd ~/dev/
Now using node v0.10.44
$ cd ~/dev/ts-sandbox-2/
Found '~/dev/ts-sandbox-2/.nvmrc' with version <4>
Now using node v4.4.7
$ cd ~/dev/
Now using node v0.10.44

Fun right?

@kirkstrobeck
Copy link

Great to see work on this. Any chance in the absence of .nvmrc, it could use the engine setting in package.json. I would prefer to set the version once for the whole repo.

@kirkstrobeck
Copy link

Also, I made https://www.npmjs.com/package/strict-version to help with forcing version

@ljharb
Copy link
Member

ljharb commented Oct 23, 2016

@kirkstrobeck that would require parsing JSON in POSIX, as well as handling semver ranges. It's not feasible at this time.

@kirkstrobeck
Copy link

Sure, but I’ve got to believe that work is already done and just needs to be added as a lib, how does npm handle it?

@ljharb
Copy link
Member

ljharb commented Oct 23, 2016

npm does it in JS, since they have node available. nvm has to operate without node. nvm also is a single sourced shell file - it can't have any "libs" that aren't built into all POSIX systems.

jrhorn424 pushed a commit to grohaus/schism-client that referenced this issue Mar 12, 2017
This supports auto-switching of node versions with a shell hook or other
tools. For example:

-   [zsh hook](nvm-sh/nvm#110 (comment))
-   [avn](https://github.com/wbyoung/avn)
@mockdeep
Copy link

Inspired by @jusopi, I wanted to automate setting up the correct node version. I chose to parse package.json using the command line utility jq. Also removed the directory check, since if I'm cding to the same directory, chances are I want to run these commands again regardless:

function enter_directory {
    if [[ -e "package.json" ]]; then
        nvm use `cat package.json | jq -r '.engines.node'`;
    fi
}

export PROMPT_COMMAND=enter_directory

Not super robust, but keeps from having to duplicate node version in different places. We pin down an exact version in our projects anyway.

@mockdeep
Copy link

nm, I need that directory check, as it runs every time a prompt is shown:

function enter_directory {
    if [[ $PWD == $PREV_PWD ]]; then
        return
    fi
    PREV_PWD=$PWD
    if [[ -e "package.json" ]]; then
        nvm use `cat package.json | jq -r '.engines.node'`;
    fi
}

@Vadorequest
Copy link

Vadorequest commented Mar 25, 2017 via email

@olalonde
Copy link

Would be nice if nvm would understand semver, e.g.:

nvm install ">6"
nvm use ">6"
# etc.

@ljharb
Copy link
Member

ljharb commented Jun 16, 2017

@olalonde Writing a semver parser in posix is not a trivial task. However, nvm install 6 will do what you want, as will nvm install 6.1, or nvm install --lts (lts/* in .nvmrc), or nvm install --lts=boron (lts/boron in .nvmrc), or nvm install node (which is the latest version).

@greyhawk
Copy link

fish shell:

functions cd
function cd --description 'Change directory'
	set -l MAX_DIR_HIST 25

    if test (count $argv) -gt 1
        printf "%s\n" (_ "Too many args for cd command")
        return 1
    end

    # Skip history in subshells.
    if status --is-command-substitution
        builtin cd $argv
        return $status
    end

    # Avoid set completions.
    set -l previous $PWD

    if test "$argv" = "-"
        if test "$__fish_cd_direction" = "next"
            nextd
        else
            prevd
        end
        return $status
    end

    builtin cd $argv
    set -l cd_status $status

    if test $cd_status -eq 0 -a "$PWD" != "$previous"
        set -q dirprev[$MAX_DIR_HIST]
        and set -e dirprev[1]
        set -g dirprev $dirprev $previous
        set -e dirnext
        set -g __fish_cd_direction prev
    end

    if test -f .nvmrc
      nvm use
    end

    return $cd_status
end

@ljharb
Copy link
Member

ljharb commented Aug 23, 2017

@greyhawk nvm doesn't support fish, so i'm not sure how that's working.

@d4nyll
Copy link
Contributor

d4nyll commented Jul 15, 2018

I'll add my variation of the a Bash alias you can use to perform this automatic switching.

cdnvm(){
  cd $1
  if [[ -f .nvmrc && -s .nvmrc && -r .nvmrc ]]; then
    <.nvmrc nvm install;
  elif [[ $(nvm current) != $(nvm version default) ]]; then
    nvm use default;
  fi
}
alias cd='cdnvm'

As a one-liner:

alias cd='cdnvm(){ cd $1; if [[ -f .nvmrc && -s .nvmrc && -r .nvmrc ]]; then <.nvmrc nvm install; elif [[ $(nvm current) != $(nvm version default) ]]; then nvm use default; fi; };cdnvm'

To run on your shell:

echo 'alias cd='\''cdnvm(){ cd $1; if [[ -f .nvmrc && -s .nvmrc && -r .nvmrc ]]; then <.nvmrc nvm install; elif [[ $(nvm current) != $(nvm version default) ]]; then nvm use default; fi; };cdnvm'\''' >> ~/.bashrc

asciicast

@Jamesford
Copy link

If you're using fish, but don't want to modify the cd function.

function __use_nvmrc --on-variable PWD --description 'Use .nvmrc if it exists'
  # https://github.com/fish-shell/fish-shell/issues/583#issuecomment-13758325
  status --is-command-substitution; and return

  # https://github.com/creationix/nvm/issues/110#issuecomment-324228987
  if test -f .nvmrc
    nvm use
  end
end

@ljharb greyhawk might be using this oh-my-fish plugin for nvm. Easy way to get nvm working in fish shells.

@d4nyll
Copy link
Contributor

d4nyll commented Aug 29, 2018

I've updated my last alias. It's longer but is smarter about how it finds the .nvmrc file.

find-up () {
    path=$(pwd)
    while [[ "$path" != "" && ! -e "$path/$1" ]]; do
        path=${path%/*}
    done
    echo "$path"
}

cdnvm(){
    cd $@;
    nvm_path=$(find-up .nvmrc | tr -d '[:space:]')

    # If there are no .nvmrc file, use the default nvm version
    if [[ ! $nvm_path = *[^[:space:]]* ]]; then

        declare default_version;
        default_version=$(nvm version default);

        # If there is no default version, set it to `node`
        # This will use the latest version on your machine
        if [[ $default_version == "N/A" ]]; then
            nvm alias default node;
            default_version=$(nvm version default);
        fi

        # If the current version is not the default version, set it to use the default version
        if [[ $(nvm current) != "$default_version" ]]; then
            nvm use default;
        fi

        elif [[ -s $nvm_path/.nvmrc && -r $nvm_path/.nvmrc ]]; then
        declare nvm_version
        nvm_version=$(<"$nvm_path"/.nvmrc)

        # Add the `v` suffix if it does not exists in the .nvmrc file
        if [[ $nvm_version != v* ]]; then
            nvm_version="v""$nvm_version"
        fi

        # If it is not already installed, install it
        if [[ $(nvm ls "$nvm_version" | tr -d '[:space:]') == "N/A" ]]; then
            nvm install "$nvm_version";
        fi

        if [[ $(nvm current) != "$nvm_version" ]]; then
            nvm use "$nvm_version";
        fi
    fi
}
alias cd='cdnvm'

Also see https://asciinema.org/a/191898

@btoueg
Copy link

btoueg commented Nov 29, 2018

To elaborate on @greyhawk great fish alias, here is another version that:

  • find recursively .nvmrc in parent directories
  • load nvm version only if necessary
  • restore default version if no .nvmrc found

$HOME/.config/fish/functions/find-up.fish

function find-up --description 'Find file recursively in parent directory'
    set path (pwd)
    while test "$path" != "/" ; and not test -f "$path/$argv[1]"
        set path (dirname $path)
    end
    if test -f "$path/$argv[1]"
        echo "$path/$argv[1]"
    else
        echo ""
    end
end

$HOME/.config/fish/functions/cd.fish

function cd --description 'alias cd cd'

    set -l MAX_DIR_HIST 25

    if test (count $argv) -gt 1
        printf "%s\n" (_ "Too many args for cd command")
        return 1
    end

    # Skip history in subshells.
    if status --is-command-substitution
        builtin cd $argv
        return $status
    end

    # Avoid set completions.
    set -l previous $PWD

    if test "$argv" = "-"
        if test "$__fish_cd_direction" = "next"
            nextd
        else
            prevd
        end
        return $status
    end

    builtin cd $argv
    set -l cd_status $status

    if test $cd_status -eq 0 -a "$PWD" != "$previous"
        set -q dirprev[$MAX_DIR_HIST]
        and set -e dirprev[1]
        set -g dirprev $dirprev $previous
        set -e dirnext
        set -g __fish_cd_direction prev
    end

    set loaded_version (node --version)
    set nvmrc (find-up '.nvmrc')
    if test -f $nvmrc
        set nvmrc_version (cat $nvmrc)
        if test $loaded_version != $nvmrc_version
            nvm use --silent
        end
    else
        if string match -q -- "*/.nvm/*" $PATH
            nvm unload
        end
    end

    return $cd_status
end

EDIT

After playing with @Jamesford's solution, I think it's even more powerful!

Very interesting thread 🎉

@broven
Copy link

broven commented Mar 7, 2019

https://github.com/creationix/nvm#zsh

@ljharb
Copy link
Member

ljharb commented Feb 16, 2021

@b-long no, i would not consider it, because it's been implemented for almost 7 years: #110 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature requests I want a new feature in nvm!
Projects
None yet
Development

No branches or pull requests