Skip to content

Instantly share code, notes, and snippets.

@jart
Last active December 6, 2020 10:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jart/0fbfb3a17ce1163882e317b598b31b8a to your computer and use it in GitHub Desktop.
Save jart/0fbfb3a17ce1163882e317b598b31b8a to your computer and use it in GitHub Desktop.
NPM Dependency Calculator
# NPM Dependency Calculator
#
# Author: Justine Tunney <jart@google.com>
# Last Updated: 2016-09-22
#
# This is a .bashrc addition that lets you inspect the transitive closure of
# dependencies for an NPM package. It does not require NPM or node.js to be
# installed on your system. It takes into consideration the fact that NPM
# does not resolve diamond dependencies. It goes super fast.
#
# This tool is useful because NPM packages have a ridiculous number of
# dependencies. It's not uncommon for a modern web app written with Node
# tooling to have thousands of dependencies. And they're all written by
# random people on the Internet. Even if these people had best intentions,
# NPM doesn't support two-factor authentication, checksums, or signatures.
# It only started using HTTPS a few months ago.
#
# Take for instance Express, which is a commonly used web framework in the
# Node.js community. It markets itself as the "Fast, unopinionated,
# minimalist web framework" but it actually has 42 dependencies:
#
# $ npm-transitive-deps express | wc -l
# 43
#
# If we were considering using Express for our project, we would also need
# to know what licenses all these dependencies have:
#
# $ npm-transitive-licenses express
# bsd-3-clause
# isc
# mit
npm-info() {
local usage="Usage: ${FUNCNAME} NAME [VERSION]"
local name="${1:?${usage}}"
local version="${2:-latest}"
local cachedir="${HOME}/.npm-json"
local cache=""
if [[ "${name}" =~ ^@ ]]; then
# TODO(jart): How do we fetch these weird scoped packages?
echo "{}"
return
fi
if [[ -e "${cachedir}/${name}-${version}.json" ]]; then
cat "${cachedir}/${name}-${version}.json"
else
mkdir -p "${cachedir}/tmp" || return 1
local tmp
tmp="$(mktemp ${cachedir}/tmp/${FUNCNAME}.XXXXXXXXXX)" || return 1
printf "\e[35mfetching https://registry.npmjs.org/%s/%s\e[0m\n" "${name}" "${version}" >&2
curl -s "https://registry.npmjs.org/${name}/${version}" \
| json-pretty >"${tmp}"
if [[ "${PIPESTATUS[0]}" -ne 0 || "${PIPESTATUS[1]}" -ne 0 ]]; then
rm -f "${tmp}"
return 1
else
local real_version
real_version="$(json-get version <"${tmp}")" || return 1
cat "${tmp}"
mv -f "${tmp}" "${cachedir}/${name}-${real_version}.json"
if [[ "${version}" != "${real_version}" ]]; then
ln -sf "${name}-${real_version}.json" \
"${cachedir}/${name}-${version}.json"
fi
fi
fi
}
npm-transitive-deps() {
local usage="Usage: ${FUNCNAME} NAME [VERSION]"
local name="${1:?${usage}}"
local version="${2:-latest}"
version="$(npm-info "${name}" "${version}" | json-get version)" || return 1
(
echo "${name} ${version}"
while read iname iversion a b c; do
npm-transitive-deps "${iname}" "${iversion}${a}${b}${c}" &
done < <(npm-info "${name}" "${version}" \
| json-get dependencies \
| json-keyvalues)
wait
) | sort -u
}
npm-transitive-licenses() {
local usage="Usage: ${FUNCNAME} NAME [VERSION]"
local name="${1:?${usage}}"
local version="${2:-latest}"
(
while read name version; do
npm-info "${name}" "${version}" \
| json-get license \
| tr \ A-Z _a-z
done < <(npm-transitive-deps "${name}" "${version}")
) | sort -u
}
npm-transitive-maintainers() {
local usage="Usage: ${FUNCNAME} NAME [VERSION]"
local name="${1:?${usage}}"
local version="${2:-latest}"
(
while read name version; do
npm-info "${name}" "${version}" \
| json-get maintainers \
| tr \ A-Z _a-z
done < <(npm-transitive-deps "${name}" "${version}")
) | sort -u
}
typescript-dependencies() {
(
npm-transitive-deps clang-format '^1.0.45' &
npm-transitive-deps typescript '^2.0.0' &
wait
) | sort -u
}
tensorboard-dependencies() {
(
npm-transitive-deps browserify '^13.1.0' &
npm-transitive-deps gulp '~3.9.0' &
npm-transitive-deps gulp-bower '0.0.13' &
npm-transitive-deps gulp-clean-compiled-typescript '~1.0.1' &
npm-transitive-deps gulp-cli '^1.1.0' &
npm-transitive-deps gulp-concat '^2.6.0' &
npm-transitive-deps gulp-filter '~3.0.1' &
npm-transitive-deps gulp-header '~1.7.1' &
npm-transitive-deps gulp-rename '~1.2.2' &
npm-transitive-deps gulp-replace '~0.5.4' &
npm-transitive-deps gulp-server-livereload '~1.5.4' &
npm-transitive-deps gulp-tslint '~4.2.2' &
npm-transitive-deps gulp-typescript '~2.10.0' &
npm-transitive-deps gulp-util '~3.0.7' &
npm-transitive-deps gulp-vulcanize '~6.1.0' &
npm-transitive-deps merge2 '~0.3.6' &
npm-transitive-deps minimist '~1.2.0' &
npm-transitive-deps tsify '^0.14.8' &
npm-transitive-deps tslint '^3.2.1' &
npm-transitive-deps typescript '^2.0.0' &
npm-transitive-deps typings '~1.0.4' &
npm-transitive-deps vinyl-source-stream '^1.1.0' &
npm-transitive-deps vulcanize '^1.14.0' &
npm-transitive-deps web-component-tester '4.2.2' &
wait
) | sort -u
}
json-get() {
local usage="Usage: ${FUNCNAME} KEY"
local key="${1:?${usage}}"
python -c "
import json, sys
try:
blob = json.load(sys.stdin)
if blob and isinstance(blob, dict) and '${key}' in blob:
x = blob['${key}']
if isinstance(x, list) or isinstance(x, dict):
json.dump(x, sys.stdout)
print
else:
print x
except ValueError:
pass"
}
json-keys() {
python -c "
import json, sys
try:
blob = json.load(sys.stdin)
if blob and isinstance(blob, dict):
print '\n'.join(blob.keys())
except ValueError:
pass"
}
json-keyvalues() {
python -c "
import json, sys
try:
blob = json.load(sys.stdin)
if blob and isinstance(blob, dict):
for k, v in blob.items():
print k, v
except ValueError:
pass"
}
json-pretty() {
python -c '
import json, sys
try:
json.dump(json.load(sys.stdin), sys.stdout, indent=2)
print
except ValueError:
pass'
}
@ArunJRK
Copy link

ArunJRK commented May 5, 2020

How can I use this? I'm new to linux.

@jart
Copy link
Author

jart commented May 5, 2020

Theo de Raadt is trying to leverage the safety and security of the OpenBSD community by WIN32'ing binary interfaces. https://lwn.net/Articles/806776/ Go see what they're doing since that's a better place to probably get what you want.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment