|
#! /usr/bin/env bash |
|
set -euo pipefail |
|
IFS=$'\n\t' |
|
|
|
#/ |
|
#|------------------------------------------------------------------------------ |
|
#| Add a Dependency to a Managed Package |
|
#|------------------------------------------------------------------------------ |
|
#| |
|
#| Note: Depends on `json` cli from npm! |
|
#| eg. `npm i -g json` |
|
#| |
|
#| Override the default `yarn add` behaviour, to make sure that dependencies |
|
#| are installed & hoisted by lerna. |
|
#| |
|
#| If dependencies are not hoisted by lerna, then we can get duplicate singleton |
|
#| issues during js runtime. |
|
#| |
|
#| Note: This script must be run in the context of the 'preinstall' lifecycle hook |
|
#| in yarn (not npm - it does not work in npm until v5). |
|
#| |
|
#/ |
|
|
|
if [ -L "$0" ]; then |
|
_linked_dir=$(dirname "$0") |
|
_link_dir=$(dirname "$(readlink $0)") |
|
script_dir=$(cd "$_linked_dir"; cd "$_link_dir"; pwd) |
|
else |
|
script_dir=$(dirname "$0") |
|
fi |
|
source "$script_dir/bash-lib.sh" |
|
|
|
package_dir=$(pwd) |
|
package_json="$package_dir/package.json" |
|
monorepo_root_dir=$(cd "$package_dir/../../"; pwd) |
|
yarn_inner_pid="$(ps -p $$ -o ppid= | xargs)" |
|
yarn_cli_pid="$(ps -p "$yarn_inner_pid" -o ppid= | xargs)" |
|
yarn_cli_cmd="$(ps -p "$yarn_cli_pid" -o args=)" |
|
PATH="$PATH:$script_dir/node_modules/.bin:$monorepo_root_dir/node_modules/.bin:$package_dir/node_modules/.bin" |
|
|
|
function js () { |
|
node -pe "${1:?}" |
|
} |
|
function pr () { |
|
>&2 printf " > ${1:-""}" |
|
>&2 echo |
|
} |
|
|
|
>&2 echo |
|
package_name=`js "require('$package_dir/package.json').name"` |
|
should_catch_cmd=`js "/yarn.+add/.test('$yarn_cli_cmd')"` |
|
is_being_added=`js "'$yarn_cli_cmd'.includes('$package_name')"` |
|
if [ "$should_catch_cmd" = "false" ] || [ "$is_being_added" = "true" ]; then |
|
pr "Ignoring \`$npm_lifecycle_event\` initiator." |
|
echo |
|
exit 0 |
|
else |
|
pr "${green}Overriding package management.${reset}" |
|
pr |
|
fi |
|
|
|
function exit_yarn () { |
|
# The extend of this script is to overload yarn behaviour - once run, we don't |
|
# care about yarn continuing. |
|
pr "Exiting from yarn lifecycles... (might see a kill signal)" |
|
if [ $(js "'$(ps -p $$ -oargs=)'.includes('zsh')") = "false" ]; then echo; fi |
|
kill -9 "$yarn_cli_pid" "$yarn_inner_pid" > /dev/null |
|
tput cuu1 |
|
tput el |
|
} |
|
|
|
packages="" |
|
# Go through the args from `yarn add`, extracting the package names to add |
|
function get_packages_from_cmd () { |
|
pr "Extracting dependency names & versions..." |
|
has_reached_past_cmd="false" |
|
for pkg in $(echo "$yarn_cli_cmd" | tr " " "\n"); do |
|
if [ "$has_reached_past_cmd" = "true" ]; then |
|
if [ ! $(grep '^-' <<<"$pkg") ]; then |
|
pkg_split=`js "'$pkg'.split(/(?!^)@/)"` |
|
pkg_name=`js "${pkg_split}[0]"` |
|
pkg_version=`js "${pkg_split}[1] || ''"` |
|
|
|
if [ "$pkg_version" = "" ]; then |
|
pkg_ver_json="$(yarn info "$pkg_name" dist-tags.latest --json 2>&1)" |
|
response_type=$(json <<<"$pkg_ver_json" "type") |
|
response_data=$(json <<<"$pkg_ver_json" "data") |
|
|
|
if [ "$response_type" != "inspect" ]; then |
|
pr "Unexpected response for latest version of ${dim}\`$pkg_name\`${reset}." |
|
pr "Error: $response_data" |
|
exit_yarn |
|
exit 1 |
|
fi |
|
|
|
pkg_version="${npm_config_save_prefix}${response_data}" |
|
fi |
|
|
|
if [ "$packages" = "" ]; then |
|
packages+="${pkg_name}:${pkg_version}" |
|
else |
|
packages+=";${pkg_name}:${pkg_version}" |
|
fi |
|
fi |
|
else |
|
if [ "$pkg" = "add" ]; then |
|
has_reached_past_cmd="true" |
|
fi |
|
fi |
|
done |
|
} |
|
|
|
# Find which kind of dependency to add the packages as |
|
function get_dependency_type_from_cmd () { |
|
dep_type="dependencies" |
|
has_reached_past_cmd="false" |
|
for arg in $(echo "$yarn_cli_cmd" | tr " " "\n"); do |
|
if [ "$has_reached_past_cmd" = "true" ]; then |
|
if [ $(grep '^-' <<<"$arg") ]; then |
|
arg_name=`js "'$arg'.replace(/^-+/, '')"` |
|
case "$arg_name" in |
|
"D" | "dev") dep_type="devDependencies" ;; |
|
"P" | "peer") dep_type="peerDependencies" ;; |
|
"O" | "optional") dep_type="optionalDependencies" ;; |
|
esac |
|
break |
|
fi |
|
else |
|
if [ "$arg" = "add" ]; then |
|
has_reached_past_cmd="true" |
|
fi |
|
fi |
|
done |
|
} |
|
|
|
# Create the js code to execute with the `json` cli |
|
function add_packages_to_packagejson () { |
|
dep_body="" |
|
for pkg in $(echo "$packages" | tr ";" "\n"); do |
|
dep_body+="$(js " |
|
'$pkg'.replace( |
|
/^(.*):(.*)$/, |
|
'\'\$1\': \'\$2\',' |
|
) |
|
")" |
|
done |
|
js_script_add_deps=" |
|
let { assign, keys } = Object; |
|
let depsAll = assign({}, this.${dep_type}, {${dep_body}}); |
|
let depsKeys = keys(depsAll).sort(); |
|
this.${dep_type} = depsKeys.reduce((deps, key) => { |
|
deps[key] = depsAll[key] |
|
return deps; |
|
}, {}); |
|
" |
|
|
|
# Edit the package.json in-place, with the new dependencies |
|
json -I -q -f "$package_json" -e "$js_script_add_deps" |
|
} |
|
|
|
function install_packages () { |
|
# The crucial next step is to download/install the new packages with lerna |
|
echo "$monorepo_root_dir" |
|
cd "$monorepo_root_dir" |
|
yarn install-deps |
|
} |
|
|
|
if ! lib_cli_exists json; then |
|
exit_yarn |
|
exit 1 |
|
fi |
|
|
|
get_packages_from_cmd |
|
get_dependency_type_from_cmd |
|
|
|
pr "Adding to \`${dim}${dep_type}${reset}\`:" |
|
for pkg in $(echo "$packages" | tr ";" "\n"); do |
|
name="${bold}$(js "'$pkg'.replace(/:.*/, '')")${reset}" |
|
ver="${dim}@$(js "'$pkg'.replace(/.*:/, '')")${reset}" |
|
pr " ${name}${ver}" |
|
done |
|
pr |
|
|
|
add_packages_to_packagejson |
|
pr "${underlined}package.json${reset} updated" |
|
pr |
|
|
|
pr "Installing packages with ${bold}lerna${reset}..." |
|
echo |
|
install_packages |
|
echo |
|
|
|
exit_yarn |
|
exit 0 |