#!/usr/bin/env bash # npmTimeMachine # Quickly get package versions where they would have been on a certain date # Can be useful for getting out of dependency hell by slowly working your way forward in time # as if you had stayed up to date in the first place. # # The script will run and find the appropriate package versions based on public npm publish data # # Usage: # Go to the directory with the package.json and run # `npmTimeMachine ` # After running it will output _dependencies.json # Check through this file and then copy into your package.json # Sweet salvation from the dependency hell :) # # MIT License # # Copyright (c) 2021 Nate Faber for Artory, Inc # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. set -euo pipefail upgradeDate=$(date -d "$1" '+%Y-%m-%d') upgradeDateEod="${upgradeDate}T23:59:59.999Z" shift 1 fileout="./${upgradeDate}_dependencies.json" tmpfile=$(mktemp) function finish() { rm "$tmpfile" } trap finish EXIT echo "{" | tee "$tmpfile" function calculate() { case "$1" in deps) depList="$(jq -r '.dependencies | keys | .[]' package.json)" depVersions=$(jq -r '.dependencies' package.json) ;; devDeps) depList="$(jq -r '.devDependencies | keys | .[]' package.json)" depVersions=$(jq -r '.devDependencies' package.json) ;; *) echo "bad calculate option" return 1 ;; esac firstValue=true for dep in $depList; do currentVersion=$(jq -r '."'"$dep"'"' <<<"$depVersions") # Comma work-around for valid JSON if [[ -z $firstValue ]]; then echo "," | tee -a "$tmpfile" fi firstValue= # Some packages (custom links for example) don't have npm, just ignore and continue set +e if ! (npm view "$dep" time >/dev/null 2>&1); then set -e echo -en "\"$dep\": \"$currentVersion\"" | tee -a "$tmpfile" continue fi set -e # Get versions that were published on or before a date, and filter out versions with strings in the names (removes beta, experimental, etc) version=$(npm view "$dep" time --json | jq -r ' [del(.created, .modified) | to_entries[] | select(.value<="'"$upgradeDateEod"'") | select(.key|test("^[0-9.]+$")).key] | last') # Add the preexisting upgrade policy to the new version if [ "${currentVersion:0:1}" = '^' ]; then initialSymbol="^" elif [ "${currentVersion:0:1}" = '~' ]; then initialSymbol="~" else initialSymbol= fi echo -en "\"${dep}\": \"${initialSymbol}${version}\"" | tee -a "$tmpfile" done } echo '"dependencies": {' | tee -a "$tmpfile" calculate deps echo -e "\n}," | tee -a "$tmpfile" echo '"devDependencies": {' | tee -a "$tmpfile" calculate devDeps echo -e "\n}" | tee -a "$tmpfile" echo "}" | tee -a "$tmpfile" # Pass through jq for formatting and verification of proper json jq . "$tmpfile" >"$fileout"