#!/usr/bin/env bash

# This script is like npx but also works for yarn pnp projects, and
# never tries to install anything. It is very fast.
#
# The script takes as arguments a command to execute and the arguments
# to pass to it. If the command is installed as a binary by an npm
# module in the current project, then that binary is used. Otherwise,
# the script is execed as normal from $PATH. In either case, the
# working directory is preserved.

if (( "$#" == 0 )); then
    echo >&2 "usage: apheleia-npx CMD [ARG...]"
    exit 1
fi

# location of this script
scripts_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
pnp_bin="${scripts_dir}/pnp-bin.js"

# This function prints the name of the current directory if it
# contains a file or directory named after the first argument, or the
# parent directory if it contains such a file, or the parent's parent,
# and so on. If no such file is found it returns nonzero.
# https://unix.stackexchange.com/a/22215
find_upwards() {
    fname="$1"

    path="${PWD}"
    while [[ -n "${path}" && ! -e "${path}/${fname}" ]]; do
        path="${path%/*}"
    done
    [[ -n "${path}" ]] && echo "${path}"
}

dir="$(find_upwards package.json)"

if [[ -d $dir ]]; then
    cd "$dir"

    pnp_root="$(find_upwards .pnp.cjs)"
    npm_root="$(find_upwards node_modules)"

    if [[ -n ${pnp_root} ]]; then
        # trying pnp
        pnp_path="${pnp_root}/.pnp.cjs"
        bin="$(${pnp_bin} "${pnp_path}" "$1")"
        # note: $bin might not be on the real filesystem,
        #   might be in a zip archive
        if [[ -n $bin ]]; then
            node="$(which node)"
            if [[ -f "${pnp_root}/.pnp.loader.mjs" ]]; then
                exec ${node} --require "${pnp_path}" \
                    --loader "${pnp_root}/.pnp.loader.mjs" "${bin}" "${@:2}"
            fi
            exec ${node} --require "${pnp_path}" "${bin}" "${@:2}"
        fi
    elif [[ -n ${npm_root} ]]; then
        # trying npm
        node_modules_paths=()
        while IFS='' read -r line; do
            node_modules_paths+=("$line")
        done < <(node -e 'console.log(require.resolve.paths("").join("\n"))')
        for path in "${node_modules_paths[@]}"; do
            if [[ -x "${path}/.bin/$1" ]]; then
                exec "${path}/.bin/$1" "${@:2}"
            fi
        done
    fi
fi

# Fall back to executing the command if it's installed and on the user's $PATH
exec "$@"
