├── bin ├── list-legacy-filenames ├── parse-legacy-file ├── post-plugin-update ├── list-all ├── postinstall └── install ├── lib ├── commands │ ├── command-resolve │ ├── command-nodebuild │ └── command-update-nodebuild └── utils.sh ├── .github └── workflows │ └── workflow.yml ├── LICENSE ├── .gitignore ├── .gitattributes ├── shims └── npm └── README.md /bin/list-legacy-filenames: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo .nvmrc .node-version 4 | -------------------------------------------------------------------------------- /bin/parse-legacy-file: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu -o pipefail 4 | 5 | # shellcheck source=../lib/utils.sh 6 | source "$(dirname "$0")/../lib/utils.sh" 7 | 8 | resolve_version "$(cat "$1")" 9 | -------------------------------------------------------------------------------- /bin/post-plugin-update: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | set -eu -o pipefail 4 | 5 | # shellcheck source=../lib/utils.sh 6 | source "$(dirname "$0")/../lib/utils.sh" 7 | 8 | migrate_aliases_mechanism() { 9 | local inst= 10 | for inst in "$(asdf_data_dir)/installs/nodejs/"*; do 11 | if [[ "$(basename "$inst")" = lts* && -L "$inst" ]]; then 12 | rm "$inst" 13 | fi 14 | done 15 | } 16 | 17 | migrate() { 18 | migrate_aliases_mechanism 19 | } 20 | 21 | migrate 22 | -------------------------------------------------------------------------------- /lib/commands/command-resolve: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu -o pipefail 4 | 5 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../utils.sh" 6 | 7 | legacy_strat="${ASDF_NODEJS_LEGACY_FILE_DYNAMIC_STRATEGY-}" 8 | queries=() 9 | 10 | for arg; do 11 | case "$arg" in 12 | --latest-installed) 13 | legacy_strat=latest_installed 14 | ;; 15 | 16 | --latest-available) 17 | legacy_strat=latest_available 18 | ;; 19 | 20 | *) 21 | queries+=("$arg") 22 | ;; 23 | esac 24 | done 25 | 26 | for query in "${queries[@]}"; do 27 | ASDF_NODEJS_LEGACY_FILE_DYNAMIC_STRATEGY="$legacy_strat" resolve_version "$query" 28 | done 29 | -------------------------------------------------------------------------------- /bin/list-all: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # simulate tac on macOS 4 | if ! command -v tac &>/dev/null; then 5 | tac() { 6 | cat -n "$@" | sort -nrk1 | cut -f2- 7 | } 8 | fi 9 | 10 | set -o nounset -o pipefail -o errexit 11 | 12 | # shellcheck source=../lib/utils.sh 13 | source "$(dirname "$0")/../lib/utils.sh" 14 | 15 | function print_only_fully_numeric_items() { 16 | local version_numbers="$1" 17 | grep -E '^[0-9.]+$' <<< "$version_numbers" 18 | } 19 | 20 | try_to_update_nodebuild >&2 21 | 22 | all_definitions_from_node_build="$(nodebuild_wrapped --definitions)" 23 | 24 | # Print 25 | echo $( 26 | print_only_fully_numeric_items "$all_definitions_from_node_build" 27 | ) 28 | -------------------------------------------------------------------------------- /bin/postinstall: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | # This script only exists to clean the old mechanism of auto-reshimming by npm .hooks from older installations 4 | # FIXME: Remove this file given enough time to migrate most users 5 | 6 | set -eu 7 | set -o pipefail 8 | 9 | plugin_name="$(basename "$(dirname "$(dirname "${BASH_SOURCE[0]}")")")" 10 | 11 | printf " 12 | This asdf installation of nodejs is still using a older mechanism of auto-reshimming 13 | We are now removing the older mechanism of all $plugin_name installations... 14 | " >&2 15 | 16 | for version in $(asdf list "$plugin_name"); do 17 | hook="$(asdf where "$plugin_name" "$version")/.npm/lib/node_modules/.hooks/postinstall" 18 | 19 | if [ -x "$hook" ]; then 20 | rm "$hook" 21 | rmdir "$(dirname "$hook")" || : &> /dev/null 22 | fi 23 | done 24 | -------------------------------------------------------------------------------- /.github/workflows/workflow.yml: -------------------------------------------------------------------------------- 1 | name: Main workflow 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | schedule: 8 | - cron: 0 0 * * 5 9 | 10 | jobs: 11 | plugin_test: 12 | strategy: 13 | matrix: 14 | os: 15 | - macos-latest 16 | - ubuntu-latest 17 | 18 | runs-on: ${{ matrix.os }} 19 | 20 | steps: 21 | - name: Checkout code 22 | uses: actions/checkout@v2 23 | 24 | - name: Install system packages on Ubuntu 25 | if: ${{ runner.os == 'Linux' }} 26 | run: sudo apt-get install curl 27 | 28 | - name: Install system packages on macOS 29 | if: ${{ runner.os == 'macOS' }} 30 | run: brew install coreutils 31 | 32 | - name: Test plugin 33 | uses: asdf-vm/actions/plugin-test@v1 34 | with: 35 | command: node --version 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (C) 2015-2017 Akash Manohar J 4 | Copyright (C) 2017 Robin Schneider 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/commands/command-nodebuild: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu -o pipefail 4 | 5 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../utils.sh" 6 | 7 | : "${ASDF_NODEJS_NODEBUILD_HOME=$ASDF_NODEJS_PLUGIN_DIR/.node-build}" 8 | : "${ASDF_NODEJS_CONCURRENCY=$(((${ASDF_CONCURRENCY:-1} + 1) / 2))}" 9 | 10 | # node-build environment variables being overriden by asdf-nodejs 11 | export NODE_BUILD_CACHE_PATH="${NODE_BUILD_CACHE_PATH:-$ASDF_NODEJS_CACHE_DIR/node-build}" 12 | 13 | if [ "$NODEJS_ORG_MIRROR" ]; then 14 | export NODE_BUILD_MIRROR_URL="$NODEJS_ORG_MIRROR" 15 | fi 16 | 17 | if [[ "${ASDF_NODEJS_CONCURRENCY-}" =~ ^[0-9]+$ ]]; then 18 | export MAKE_OPTS="${MAKE_OPTS:-} -j$ASDF_NODEJS_CONCURRENCY" 19 | export NODE_MAKE_OPTS="${NODE_MAKE_OPTS:-} -j$ASDF_NODEJS_CONCURRENCY" 20 | fi 21 | 22 | nodebuild="${ASDF_NODEJS_NODEBUILD:-$ASDF_NODEJS_NODEBUILD_HOME/bin/node-build}" 23 | args=() 24 | 25 | if ! [ -x "$nodebuild" ]; then 26 | printf "Binary for node-build not found\n" 27 | 28 | if ! [ "${ASDF_NODEJS_NODEBUILD-}" ]; then 29 | printf "Are you sure it was installed? Try running \`asdf %s update-nodebuild\` to do a local update or install\n" "$(plugin_name)" 30 | fi 31 | 32 | exit 1 33 | fi 34 | 35 | if [ "${ASDF_NODEJS_VERBOSE_INSTALL-}" ]; then 36 | args+=(-v) 37 | fi 38 | 39 | exec "$nodebuild" ${args+"${args[@]}"} "$@" 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/linux,macos,windows 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=linux,macos,windows 3 | 4 | ### Linux ### 5 | *~ 6 | 7 | # temporary files which can be created if a process still has a handle open of a deleted file 8 | .fuse_hidden* 9 | 10 | # KDE directory preferences 11 | .directory 12 | 13 | # Linux trash folder which might appear on any partition or disk 14 | .Trash-* 15 | 16 | # .nfs files are created when an open file is removed but is still being accessed 17 | .nfs* 18 | 19 | ### macOS ### 20 | # General 21 | .DS_Store 22 | .AppleDouble 23 | .LSOverride 24 | 25 | # Icon must end with two \r 26 | Icon 27 | 28 | 29 | # Thumbnails 30 | ._* 31 | 32 | # Files that might appear in the root of a volume 33 | .DocumentRevisions-V100 34 | .fseventsd 35 | .Spotlight-V100 36 | .TemporaryItems 37 | .Trashes 38 | .VolumeIcon.icns 39 | .com.apple.timemachine.donotpresent 40 | 41 | # Directories potentially created on remote AFP share 42 | .AppleDB 43 | .AppleDesktop 44 | Network Trash Folder 45 | Temporary Items 46 | .apdisk 47 | 48 | ### Windows ### 49 | # Windows thumbnail cache files 50 | Thumbs.db 51 | Thumbs.db:encryptable 52 | ehthumbs.db 53 | ehthumbs_vista.db 54 | 55 | # Dump file 56 | *.stackdump 57 | 58 | # Folder config file 59 | [Dd]esktop.ini 60 | 61 | # Recycle Bin used on file shares 62 | $RECYCLE.BIN/ 63 | 64 | # Windows Installer files 65 | *.cab 66 | *.msi 67 | *.msix 68 | *.msm 69 | *.msp 70 | 71 | # Windows shortcuts 72 | *.lnk 73 | 74 | # End of https://www.toptal.com/developers/gitignore/api/linux,macos,windows 75 | 76 | .node-build 77 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ## GITATTRIBUTES 2 | # 3 | # Details per file setting: 4 | # text These files should be normalized (i.e. convert CRLF to LF). 5 | # binary These files are binary and should be left untouched. 6 | # 7 | # Note that binary is a macro for -text -diff. 8 | ###################################################################### 9 | 10 | ## AUTO-DETECT 11 | ## Handle line endings automatically for files detected as 12 | ## text and leave all files detected as binary untouched. 13 | ## This will handle all files NOT defined below. 14 | * text=auto 15 | 16 | ## SOURCE CODE 17 | *.bats text 18 | *.css text 19 | *.htm text 20 | *.html text 21 | *.js text 22 | *.sh text 23 | *.bash text 24 | *.fish text 25 | *.elv text 26 | #### asdf/bin/* explicitly define as text 27 | *asdf text 28 | *asdf-exec text 29 | *_asdf text 30 | #### asdf/test/fixtures/* should be covered by * text=auto on L14 31 | 32 | ## DOCKER 33 | *.dockerignore text 34 | Dockerfile text 35 | 36 | ## DOCUMENTATION 37 | *.markdown text 38 | *.md text 39 | *.mdwn text 40 | *.mdown text 41 | *.mkd text 42 | *.mkdn text 43 | *.mdtxt text 44 | *.mdtext text 45 | *.txt text 46 | AUTHORS text 47 | CHANGELOG text 48 | CHANGES text 49 | CONTRIBUTING text 50 | COPYING text 51 | copyright text 52 | *COPYRIGHT* text 53 | INSTALL text 54 | license text 55 | LICENSE text 56 | NEWS text 57 | readme text 58 | *README* text 59 | TODO text 60 | 61 | ## CONFIGS 62 | .editorconfig text 63 | .gitattributes text 64 | .gitconfig text 65 | *.yaml text 66 | *.yml text 67 | -------------------------------------------------------------------------------- /lib/commands/command-update-nodebuild: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu -o pipefail 4 | 5 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../utils.sh" 6 | 7 | : "${ASDF_NODEJS_NODEBUILD_HOME=$ASDF_NODEJS_PLUGIN_DIR/.node-build}" 8 | : "${ASDF_NODEJS_NODEBUILD_REPOSITORY=https://github.com/nodenv/node-build.git}" 9 | : "${ASDF_NODEJS_NODEBUILD_BRANCH=main}" 10 | 11 | ensure_updated_project() { 12 | local pull_exit_code=0 13 | 14 | if ! [ -d "$ASDF_NODEJS_NODEBUILD_HOME" ]; then 15 | printf "Cloning node-build...\n" 16 | git clone "$ASDF_NODEJS_NODEBUILD_REPOSITORY" "$ASDF_NODEJS_NODEBUILD_HOME" --branch "$ASDF_NODEJS_NODEBUILD_BRANCH" 17 | else 18 | printf "Trying to update node-build..." 19 | git -C "$ASDF_NODEJS_NODEBUILD_HOME" fetch origin "$ASDF_NODEJS_NODEBUILD_BRANCH" >&2 || pull_exit_code=$? 20 | git -C "$ASDF_NODEJS_NODEBUILD_HOME" checkout "$ASDF_NODEJS_NODEBUILD_BRANCH" >&2 || true 21 | git -C "$ASDF_NODEJS_NODEBUILD_HOME" merge --ff-only "origin/$ASDF_NODEJS_NODEBUILD_BRANCH" >&2 || true 22 | 23 | if [ "$pull_exit_code" != 0 ]; then 24 | printf "$(colored $RED ERROR): Pulling the node-build repository exited with code %s\n" "$pull_exit_code" 25 | printf "Please check if the git repository at %s doesn't have any changes or anything\nthat might not allow a git pull\n" "$ASDF_NODEJS_NODEBUILD_HOME" 26 | exit $pull_exit_code 27 | fi 28 | 29 | # For some time, asdf-core was cleaning up the node-build directory by accident 30 | # This is a workaround to restore the files that were deleted 31 | if ! [ -f "$ASDF_NODEJS_NODEBUILD_HOME/bin/nodebuild" ]; then 32 | git -C "$ASDF_NODEJS_NODEBUILD_HOME" checkout HEAD . >&2 33 | fi 34 | 35 | printf " ok\n" 36 | fi 37 | } 38 | 39 | ensure_updated_project 40 | -------------------------------------------------------------------------------- /bin/install: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu -o pipefail 4 | 5 | # shellcheck source=../lib/utils.sh 6 | source "$(dirname "$0")/../lib/utils.sh" 7 | 8 | install_nodejs() { 9 | local install_type="$1" version="$2" install_path="$3" 10 | local args=() 11 | 12 | version=$(resolve_version "$version") 13 | 14 | if [ "$install_type" = ref ] || [ "${ASDF_NODEJS_FORCE_COMPILE-}" ]; then 15 | args+=(-c) 16 | fi 17 | 18 | try_to_update_nodebuild 19 | 20 | NODE_BUILD_CACHE_PATH="${NODE_BUILD_CACHE_PATH:-$ASDF_DOWNLOAD_PATH}" \ 21 | nodebuild_wrapped ${args+"${args[@]}"} "$version" "$install_path" 22 | } 23 | 24 | _run_for_installation() { 25 | ( 26 | if [ -r "$ASDF_NODEJS_PLUGIN_DIR/bin/exec-env" ]; then 27 | . "$ASDF_NODEJS_PLUGIN_DIR/bin/exec-env" 28 | fi 29 | 30 | env PATH="$ASDF_INSTALL_PATH/bin:$PATH" "$@" 31 | ) 32 | } 33 | 34 | 35 | install_default_npm_packages() { 36 | local pkgs_file="${ASDF_NPM_DEFAULT_PACKAGES_FILE:=$HOME/.default-npm-packages}" 37 | 38 | if ! [ -f "$pkgs_file" ]; then 39 | return 0 40 | fi 41 | 42 | npm_install() { 43 | printf "$(colored $CYAN "\nRunning the folowing install command:\n")" 44 | printf "$(colored $GREEN " \$ npm install -g %s\n")" "$*" 45 | 46 | _run_for_installation npm install -g "$@" 47 | } 48 | 49 | local args=() 50 | local line="" aux=() 51 | while read -r line; do 52 | if [[ -z "$line" ]]; then 53 | continue 54 | fi 55 | 56 | pkg_lines+=("$line") 57 | 58 | case "$line" in 59 | *' -'*|-*) 60 | # Installing previously read packages 61 | if [ "${#args[@]}" -gt 0 ]; then 62 | npm_install "${args[@]}" 63 | fi 64 | 65 | # Read current line as a command by itself 66 | args=() 67 | IFS=' ' read -ra args <<< "$line" 68 | 69 | npm_install "${args[@]}" 70 | args=() 71 | ;; 72 | *) 73 | # Accumulate packages to install 74 | IFS=' ' read -ra aux <<< "$line" 75 | args+=("${aux[@]}") 76 | ;; 77 | esac 78 | done < <(grep -v '^\s*#' "$pkgs_file") 79 | 80 | if [ "${#args[@]}" -gt 0 ]; then 81 | npm_install "${args[@]}" 82 | fi 83 | } 84 | 85 | enable_corepack() { 86 | if [ "${ASDF_NODEJS_AUTO_ENABLE_COREPACK-}" ]; then 87 | _run_for_installation corepack enable 88 | fi 89 | } 90 | 91 | install_nodejs "$ASDF_INSTALL_TYPE" "$ASDF_INSTALL_VERSION" "$ASDF_INSTALL_PATH" 92 | 93 | install_default_npm_packages \ 94 | || printf "\n$(colored $YELLOW WARNING:) An error occurred when installing the default npm packages, but Node's installation succeeded\n" 95 | 96 | enable_corepack \ 97 | || printf "\n$(colored $YELLOW WARNING:) An error occurred while enabling corepack for this version\n" 98 | 99 | asdf reshim "$(plugin_name)" "$ASDF_INSTALL_VERSION" 100 | -------------------------------------------------------------------------------- /shims/npm: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | set -eu 4 | set -o pipefail 5 | 6 | # This script wraps npm so to run `asdf reshim` after global installs and uninstalls 7 | # Any other cases are passed-through to npm 8 | 9 | this_dir=$(dirname "${BASH_SOURCE[0]}") 10 | plugin_name=$(basename "$(dirname "$this_dir")") 11 | 12 | this_dir=$(cd "$this_dir" && pwd -P) # Normalizes the directory 13 | 14 | asdf_data_dir=$(cd "${ASDF_DATA_DIR:-$HOME/.asdf}" && pwd -P) 15 | asdf_shims_dir="$asdf_data_dir/shims" 16 | 17 | plugin_dir="$asdf_data_dir/plugins/$plugin_name" 18 | 19 | should_reshim() { 20 | if [ "${ASDF_SKIP_RESHIM:-}" ]; then 21 | return 1 22 | fi 23 | 24 | local is_global= cmd= cmd_needs_reshim= 25 | local additional_bare_cmds=() 26 | 27 | for arg; do 28 | case "$arg" in 29 | -g|--global) 30 | is_global=true 31 | ;; 32 | 33 | -*) ;; # Skip other options 34 | 35 | *) 36 | if ! [ "$cmd" ]; then 37 | cmd="$arg" 38 | else 39 | additional_bare_cmds+=("$arg") 40 | fi 41 | ;; 42 | esac 43 | done 44 | 45 | case "$cmd" in 46 | # npm install aliases 47 | install|i|in|ins|inst|insta|instal|isnt|isnta|isntal|add) 48 | cmd_needs_reshim=true 49 | ;; 50 | 51 | # npm uninstall aliases 52 | uninstall|un|unlink|remove|rm|r) 53 | cmd_needs_reshim=true 54 | ;; 55 | 56 | link|ln) 57 | # Bare link installs a global package 58 | if ! [ "${additional_bare_cmds[0]-}" ]; then 59 | is_global=1 60 | cmd_needs_reshim=true 61 | fi 62 | 63 | # Links to directories also install a global package 64 | if [[ "${additional_bare_cmds[0]-}" =~ [./].* && -d "${additional_bare_cmds[0]-}" ]]; then 65 | is_global=1 66 | cmd_needs_reshim=true 67 | fi 68 | ;; 69 | esac 70 | 71 | # Implicit return 72 | [ "$is_global" ] && [ "$cmd_needs_reshim" ] 73 | } 74 | 75 | resolve_canon_npm() { 76 | local npm_location= 77 | 78 | # Try searching in current path (asdf core from 0.7 onwards adds all binaries candidates directories to PATH) 79 | # if that doesn't works (when calling the shim directly for example) it tries manually searching the binary in 80 | # the installed version provided by "asdf where" 81 | npm_location="${ASDF_NODEJS_CANON_NPM_PATH:-$(manually_search_npm_bin || search_npm_on_current_path)}" 82 | 83 | if ! [ "$npm_location" ]; then 84 | printf "asdf-nodejs couldn't find a suitable npm executable\n" 85 | printf "This is probably a problem with the plugin, please report this issue at https://github.com/asdf-vm/asdf-nodejs/issues\n" 86 | exit 1 87 | fi 88 | 89 | printf "%s\n" "$npm_location" 90 | } 91 | 92 | remove_current_dir_from_path() { 93 | local filtered_path= dir= normalized_dir= 94 | 95 | while read -rd : dir; do 96 | if [ -d "$dir" ]; then 97 | normalized_dir=$(cd "$dir" && pwd -P) 98 | 99 | if [ "$normalized_dir" = "$this_dir" ] || [ "$normalized_dir" = "$asdf_shims_dir" ]; then 100 | continue 101 | fi 102 | fi 103 | 104 | filtered_path+="$dir:" 105 | done <<< "$PATH:" 106 | 107 | printf "%s\n" "${filtered_path%:}" 108 | } 109 | 110 | search_npm_on_current_path() { 111 | local filtered_path= 112 | 113 | # Tries to prevent recursion by removing the current script and asdf-shim from PATH 114 | filtered_path=$(remove_current_dir_from_path) 115 | 116 | PATH="$filtered_path" command -v npm 117 | } 118 | 119 | manually_search_npm_bin() { 120 | local installed_dir= bin_directories= 121 | 122 | installed_dir=$(asdf where nodejs) 123 | bin_directories=(bin) 124 | 125 | if [ -x "$plugin_dir/bin/list-bin-paths" ]; then 126 | bin_directories=($("$plugin_dir/bin/list-bin-paths")) 127 | fi 128 | 129 | for bin_dir in "${bin_directories[@]}"; do 130 | if [ -x "$installed_dir/$bin_dir/npm" ]; then 131 | printf "%s\n" "$installed_dir/$bin_dir/npm" 132 | return 0 133 | fi 134 | done 135 | 136 | return 1 137 | } 138 | 139 | wrap_npm_if_reshim_is_needed() { 140 | local npm= 141 | npm=$(resolve_canon_npm) 142 | 143 | if should_reshim "$@"; then 144 | "$npm" "$@" 145 | printf "Reshimming asdf %s...\n" "$plugin_name" >&2 146 | asdf reshim "$plugin_name" 147 | else 148 | exec "$npm" "$@" 149 | fi 150 | } 151 | 152 | wrap_npm_if_reshim_is_needed "$@" 153 | -------------------------------------------------------------------------------- /lib/utils.sh: -------------------------------------------------------------------------------- 1 | # Helper functions 2 | 3 | # When in China, set $NODEJS_ORG_MIRROR: 4 | # export NODEJS_ORG_MIRROR=https://npm.taobao.org/mirrors/node/ 5 | NODEJS_ORG_MIRROR="${NODEJS_ORG_MIRROR:-https://nodejs.org/dist/}" 6 | if [ ${NODEJS_ORG_MIRROR: -1} != / ]; then 7 | NODEJS_ORG_MIRROR=$NODEJS_ORG_MIRROR/ 8 | fi 9 | 10 | export ASDF_NODEJS_PLUGIN_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) 11 | 12 | # TODO: Replace with an asdf variable once asdf starts providing the plugin name 13 | # as a variable 14 | export ASDF_NODEJS_PLUGIN_NAME=$(basename "$ASDF_NODEJS_PLUGIN_DIR") 15 | plugin_name() { 16 | printf "%s\n" "$ASDF_NODEJS_PLUGIN_NAME" 17 | } 18 | 19 | asdf_data_dir() { 20 | local data_dir 21 | 22 | if [ "${ASDF_DATA_DIR-}" ]; then 23 | data_dir="${ASDF_DATA_DIR}" 24 | elif [ "${ASDF_DIR-}" ]; then 25 | data_dir="$ASDF_DIR" 26 | else 27 | data_dir="$HOME/.asdf" 28 | fi 29 | 30 | printf "%s\n" "$data_dir" 31 | } 32 | 33 | export ASDF_NODEJS_CACHE_DIR="$(asdf_data_dir)/tmp/$ASDF_NODEJS_PLUGIN_NAME/cache" 34 | 35 | # Colors 36 | colored() { 37 | local color="$1" text="$2" 38 | printf "\033[%sm%s\033[39;49m\n" "$color" "$text" 39 | } 40 | 41 | export RED=31 GREEN=32 YELLOW=33 BLUE=34 MAGENTA=35 CYAN=36 42 | 43 | nodebuild_wrapped() { 44 | "$ASDF_NODEJS_PLUGIN_DIR/lib/commands/command-nodebuild" "$@" 45 | } 46 | 47 | try_to_update_nodebuild() { 48 | if [ "${ASDF_NODEJS_SKIP_NODEBUILD_UPDATE-}" ]; then 49 | return 50 | fi 51 | 52 | local exit_code=0 53 | 54 | "$ASDF_NODEJS_PLUGIN_DIR/lib/commands/command-update-nodebuild" 2>/dev/null || exit_code=$? 55 | 56 | if [ "$exit_code" != 0 ]; then 57 | printf " 58 | $(colored $YELLOW WARNING): Updating node-build failed with exit code %s. The installation will 59 | try to continue with already installed local defintions. To debug what went 60 | wrong, try to manually update node-build by running: \`asdf %s update nodebuild\` 61 | \n" "$exit_code" "$ASDF_NODEJS_PLUGIN_NAME" 62 | fi 63 | } 64 | 65 | # Adapted from asdf-core https://github.com/asdf-vm/asdf/blob/684f4f058f24cc418f77825a59a22bacd16a9bee/lib/utils.bash#L95-L109 66 | list_installed_versions() { 67 | local plugin_name=$1 68 | 69 | local plugin_installs_path 70 | plugin_installs_path="$(asdf_data_dir)/installs/${plugin_name}" 71 | 72 | if [ -d "$plugin_installs_path" ]; then 73 | for install in "${plugin_installs_path}"/*/; do 74 | [[ -e "$install" ]] || break 75 | basename "$install" | sed 's/^ref-/ref:/' 76 | done 77 | fi 78 | } 79 | 80 | # stolen from https://github.com/rbenv/ruby-build/pull/631/files#diff-fdcfb8a18714b33b07529b7d02b54f1dR942 81 | sort_versions() { 82 | sed 'h; s/[+-]/./g; s/.p\([[:digit:]]\)/.z\1/; s/$/.z/; G; s/\n/ /' | 83 | LC_ALL=C sort -t. -k 1,1 -k 2,2n -k 3,3n -k 4,4n -k 5,5n | awk '{print $2}' 84 | } 85 | 86 | resolve_legacy_version() { 87 | local strategy="$1" query="$2" 88 | local resolved= 89 | 90 | case "$strategy" in 91 | latest_installed) 92 | _list() { 93 | ASDF_NODEJS_SKIP_NODEBUILD_UPDATE=1 list_installed_versions nodejs | sort_versions 94 | } 95 | ;; 96 | 97 | latest_available) 98 | _list() { 99 | ASDF_NODEJS_SKIP_NODEBUILD_UPDATE=1 "$ASDF_NODEJS_PLUGIN_DIR/bin/list-all" "$query" | tr ' ' '\n' 100 | } 101 | ;; 102 | 103 | *) 104 | # Just return the original query 105 | printf "%s\n" "$query" 106 | return 107 | esac 108 | 109 | resolved=$(_list | grep "^$query" | tail -n1) 110 | 111 | if [ "$resolved" ]; then 112 | printf "%s\n" "$resolved" 113 | elif [ "$strategy" != latest_available ]; then 114 | # If no version is installed, fallback to latest_available, so `asdf install nodejs` works 115 | resolve_legacy_version latest_available "$query" 116 | else 117 | # Give up and pretty the query itself 118 | printf "%s\n" "$query" 119 | fi 120 | } 121 | 122 | resolve_version() { 123 | local query= 124 | query=$(tr '[:upper:]' '[:lower:]' <<<"${1#v}") 125 | 126 | if [[ $query = lts-* ]]; then 127 | query=$(tr - / <<<"$query") 128 | fi 129 | 130 | local nodejs_codenames=( 131 | argon:4 132 | boron:6 133 | carbon:8 134 | dubnium:10 135 | erbium:12 136 | fermium:14 137 | gallium:16 138 | hydrogen:18 139 | iron:20 140 | jod:22 141 | ) 142 | 143 | for cod_version in "${nodejs_codenames[@]}"; do 144 | local codename="${cod_version%:*}" 145 | local version_number="${cod_version#*:}" 146 | 147 | if [ "${query#lts/}" = "$codename" ]; then 148 | query="$version_number" 149 | break 150 | fi 151 | done 152 | 153 | if [ "$query" = lts ] || [ "$query" = "lts/*" ]; then 154 | query="${nodejs_codenames[${#nodejs_codenames[@]} - 1]#*:}" 155 | local all_versions 156 | all_versions=$("$ASDF_NODEJS_PLUGIN_DIR/bin/list-all" 2>/dev/null | tr ' ' '\n') 157 | query=$(echo "$all_versions" | grep "^$query\." | tail -n1) 158 | fi 159 | 160 | if [ "${ASDF_NODEJS_LEGACY_FILE_DYNAMIC_STRATEGY-}" ]; then 161 | query=$(resolve_legacy_version "$ASDF_NODEJS_LEGACY_FILE_DYNAMIC_STRATEGY" "$query") 162 | fi 163 | 164 | printf "%s\n" "$query" 165 | } 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # asdf-nodejs [![Build](https://github.com/asdf-vm/asdf-nodejs/actions/workflows/workflow.yml/badge.svg)](https://github.com/asdf-vm/asdf-nodejs/actions/workflows/workflow.yml) 2 | 3 | Node.js plugin for [asdf](https://github.com/asdf-vm/asdf) version manager 4 | 5 | ## Install 6 | 7 | After installing [asdf](https://github.com/asdf-vm/asdf), install the plugin by running: 8 | 9 | ```bash 10 | asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git 11 | ``` 12 | 13 | ## Use 14 | 15 | Check [asdf](https://github.com/asdf-vm/asdf) readme for instructions on how to install & manage versions of Node.js at a system and project level. 16 | 17 | Behind the scenes, `asdf-nodejs` utilizes [`node-build`](https://github.com/nodenv/node-build) to install pre-compiled binaries and compile from source if necessary. You can check its [README](https://github.com/nodenv/node-build/blob/master/README.md) for additional settings and some troubleshooting. 18 | 19 | When compiling a version from source, you are going to need to install [all requirements for compiling Node.js](https://github.com/nodejs/node/blob/master/BUILDING.md#building-nodejs-on-supported-platforms) (be advised that different versions might require different configurations). That being said, `node-build` does a great job at handling edge cases and compilations rarely need a deep investigation. 20 | 21 | ### Configuration 22 | 23 | `node-build` already has a [handful of settings](https://github.com/nodenv/node-build#custom-build-configuration), in additional to that `asdf-nodejs` has a few extra configuration variables: 24 | 25 | - `ASDF_NODEJS_LEGACY_FILE_DYNAMIC_STRATEGY`: Enable and choose the strategy for 26 | dynamic/partial versions in legacy version files. Either `latest_installed` or 27 | `latest_available`. For more info check the [Partial and codename versions](#partial-and-codename-versions) section 28 | - `ASDF_NODEJS_AUTO_ENABLE_COREPACK`: Enable corepack for new installations. Defaults to empty 29 | - `ASDF_NODEJS_VERBOSE_INSTALL`: Enables verbose output for downloading and building. Any value different from empty is treated as enabled. 30 | - `ASDF_NODEJS_FORCE_COMPILE`: Forces compilation from source instead of preferring pre-compiled binaries 31 | - `ASDF_NODEJS_NODEBUILD_HOME`: Home for the node-build installation, defaults to `$ASDF_DIR/plugins/nodejs/.node-build`, you can install it in another place or share it with your system 32 | - `ASDF_NODEJS_NODEBUILD`: Path to the node-build executable, defaults to `$ASDF_NODEJS_NODEBUILD_HOME/bin/node-build` 33 | - `ASDF_NODEJS_SKIP_NODEBUILD_UPDATE`: Skip trying to update nodebuild prior to 34 | list-all and install. If enabling this var, you might need to [update nodebuild manually](#manually-updating-node-build-definitions) 35 | to get newly released versions 36 | - `ASDF_NODEJS_CONCURRENCY`: How many jobs should be used in compilation. Defaults to half the computer cores 37 | - `NODEJS_ORG_MIRROR`: (Legacy) overrides the default mirror used for downloading the distibutions, alternative to the `NODE_BUILD_MIRROR_URL` node-build env var 38 | 39 | ### `.nvmrc` and `.node-version` support 40 | 41 | asdf uses a `.tool-versions` file for auto-switching between software versions. To ease migration, you can have it read an existing `.nvmrc` or `.node-version` file to find out what version of Node.js should be used. To do this, add the following to `$HOME/.asdfrc`: 42 | 43 | ``` 44 | legacy_version_file = yes 45 | ``` 46 | 47 | ## Partial and codename versions 48 | 49 | Many version managers allow you to use partial versions (e.g. `v10`) or NodeJS 50 | codenames (e.g. `lts/hydrogen`) in version files, which are resolved at runtime. 51 | However, this can be risky as it is not guaranteed that all developers will use 52 | the same version, leading to non-reproducibility. In `asdf`, we prioritize 53 | reproducibility, so you cannot use partial versions or codenames in a 54 | `.tool-versions` file. 55 | 56 | To address this, we offer an escape hatch for legacy version files. If you are 57 | comfortable with non-reproducibility issues, you can choose between strategies 58 | in a custom environment variable `ASDF_NODEJS_LEGACY_FILE_DYNAMIC_STRATEGY`. You 59 | can export this variable from your shell rc file and it will become the default 60 | behavior. 61 | 62 | > **This option is only available for legacy version files (.nvmrc and 63 | > .node-version, at the moment), for that you will need to set 64 | > `legacy_version_file` to `yes` in your .asdfrc config file. More info on the 65 | > [official docs](https://asdf-vm.com/manage/configuration.html#legacy-version-file)** 66 | > 67 | > The `.tool-versions` file will never support non-deterministic versions, if 68 | > they were supported in the past that was an unintentional side-effect 69 | 70 | The possible values for this variable are: 71 | 72 | - `latest_installed`: Will get the latest version already installed that matches 73 | the version query. Just installing a new version that matches the dynamic 74 | version would be enough to update it. If no matching version is installed it 75 | fallbacks to the latest version available to download. 76 | - `latest_available`: Will get the latest version available for installation 77 | that matches the version query, this means that when a new NodeJS version gets 78 | released you will need to install it before running any command 79 | 80 | It is important to be aware of the risks of non-reproducibility. Debugging can 81 | become more challenging and bugs may leak into production if the deployed node 82 | version differs from the one used in development. Ideally, maintainers should be 83 | encouraged to pin the version to a specific release to avoid these issues. 84 | 85 | If non-reproducibility is not a concern for you, you can use one of the 86 | following resolve scripts in your shell rc file: 87 | 88 | ```bash 89 | export ASDF_NODEJS_LEGACY_FILE_DYNAMIC_STRATEGY=latest_installed 90 | # OR 91 | export ASDF_NODEJS_LEGACY_FILE_DYNAMIC_STRATEGY=latest_available 92 | ``` 93 | 94 | > **NOTE**: Partial versions and codenames only work for legacy version files: `.node-version` and `.nvmrc`. 95 | 96 | ### Default npm Packages 97 | 98 | `asdf-nodejs` can automatically install a set of default set of npm package right after installing a Node.js version. To enable this feature, provide a `$HOME/.default-npm-packages` file that lists one package per line, for example: 99 | 100 | ``` 101 | lodash 102 | request 103 | express 104 | 105 | # .default-npm-packages can have comments, and option flags: 106 | zx --registry=https://registry.yarnpkg.com/ 107 | ``` 108 | 109 | You can specify a non-default location of this file by setting a `ASDF_NPM_DEFAULT_PACKAGES_FILE` variable. 110 | 111 | ### Running the wrapped node-build command 112 | 113 | We provide a command for running the installed `node-build` command: 114 | 115 | ```bash 116 | asdf cmd nodejs nodebuild --version 117 | ``` 118 | 119 | ### node-build advanced variations 120 | 121 | `node-build` has some additional variations aside from the versions listed in `asdf list-all nodejs` (chakracore/graalvm branches and some others). As of now, we weakly support these variations. In the sense that they are available for install and can be used in a `.tool-versions` file, but we don't list them as installation candidates nor give them full attention. 122 | 123 | Some of them will work out of the box, and some will need a bit of investigation to get them built. We are planning in providing better support for these variations in the future. 124 | 125 | To list all the available variations run: 126 | 127 | ```bash 128 | asdf cmd nodejs nodebuild --definitions 129 | ``` 130 | 131 | _Note that this command only lists the current `node-build` definitions. You might want to [update the local `node-build` repository](#updating-node-build-definitions) before listing them._ 132 | 133 | ### Manually updating node-build definitions 134 | 135 | Every new node version needs to have a definition file in the `node-build` repository. `asdf-nodejs` already tries to update `node-build` on every new version installation, but if you want to update `node-build` manually for some reason we provide a command just for that: 136 | 137 | ```bash 138 | asdf cmd nodejs update-nodebuild 139 | ``` 140 | 141 | ### Integrity/signature check 142 | 143 | In the past `asdf-nodejs` checked for signatures and integrity by querying live keyservers. `node-build`, on the other hand, checks integrity by precomputing checksums ahead of time and versioning them together with the instructions for building them, making the process a lot more streamlined. 144 | 145 | ### Resolving latest available LTS version in a script 146 | 147 | This plugin adds a custom subcommand `asdf cmd nodejs resolve lts`. If you want to know what is the latest available LTS major version number you can do this: 148 | 149 | ```sh 150 | # Before checking for aliases, update nodebuild to check for newly releasead versions 151 | asdf cmd nodejs update-nodebuild 152 | 153 | asdf cmd nodejs resolve lts 154 | # outputs: 22.13.1 155 | ``` 156 | 157 | You also have the option of forcing a resolution strategy by using the flags `--latest-installed` and `--latest-available` 158 | 159 | ```bash 160 | # Outputs the latest version installed locally which is a LTS 161 | asdf cmd nodejs resolve lts --latest-installed 162 | 163 | # Outputs the latest version available for download which is a LTS 164 | asdf cmd nodejs resolve lts --latest-available 165 | ``` 166 | 167 | ## Corepack 168 | 169 | If you're using Node.js' [corepack](https://nodejs.org/api/corepack.html) to install `yarn` or `pnpm`, you'll need to reshim after running `corepack prepare`, example: 170 | 171 | ```bash 172 | corepack enable 173 | corepack prepare pnpm@latest --activate 174 | asdf reshim nodejs 175 | ``` 176 | --------------------------------------------------------------------------------