diff --git a/.changes/tauri.js-dependency-manager.md b/.changes/tauri.js-dependency-manager.md new file mode 100644 index 000000000..7e5e33138 --- /dev/null +++ b/.changes/tauri.js-dependency-manager.md @@ -0,0 +1,6 @@ +--- +"tauri.js": minor +--- + +Adds a dependency manager command to the Node.js CLI (`tauri deps`). The manager is able to install and update Rust and the Tauri ecosystem dependencies (npm package, crates, cargo subcommands). +Usage: `tauri deps install` and `tauri deps update`. diff --git a/cli/tauri.js/bin/tauri-deps.js b/cli/tauri.js/bin/tauri-deps.js new file mode 100644 index 000000000..652d2aeb6 --- /dev/null +++ b/cli/tauri.js/bin/tauri-deps.js @@ -0,0 +1,22 @@ +async function run() { + const { + installDependencies, + updateDependencies + } = require('../dist/api/dependency-manager') + + const choice = process.argv[2] + if (choice === 'install') { + await installDependencies() + } else if (choice === 'update') { + await updateDependencies() + } else { + console.log(` + Description + Tauri dependency management script + Usage + $ tauri deps [install|update] + `) + } +} + +run() diff --git a/cli/tauri.js/bin/tauri.js b/cli/tauri.js/bin/tauri.js index 6e378375b..a34cb1412 100755 --- a/cli/tauri.js/bin/tauri.js +++ b/cli/tauri.js/bin/tauri.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -const cmds = ['init', 'dev', 'build', 'help', 'icon', 'info'] +const cmds = ['init', 'dev', 'build', 'help', 'icon', 'info', 'deps'] const cmd = process.argv[2] /** @@ -45,6 +45,8 @@ const tauri = function (command) { console.log(`Invalid command ${command}. Use one of ${cmds.join(',')}.`) } } -module.exports = { tauri } +module.exports = { + tauri +} tauri(cmd) diff --git a/cli/tauri.js/package.json b/cli/tauri.js/package.json index 7332da77e..0a0637f6e 100644 --- a/cli/tauri.js/package.json +++ b/cli/tauri.js/package.json @@ -70,6 +70,7 @@ "ms": "2.1.2", "png2icons": "2.0.1", "read-chunk": "3.2.0", + "semver": "^7.3.2", "sharp": "0.25.4", "webpack-merge": "5.0.9", "webpack-shell-plugin": "0.5.0" @@ -89,9 +90,11 @@ "@types/http-proxy": "1.17.4", "@types/imagemin": "7.0.0", "@types/imagemin-optipng": "5.2.0", + "@types/inquirer": "^6.5.0", "@types/jsdom": "16.2.3", "@types/lodash": "4.14.157", "@types/ms": "0.7.31", + "@types/semver": "^7.3.1", "@types/sharp": "0.25.0", "@types/webpack-merge": "4.1.5", "@typescript-eslint/eslint-plugin": "3.6.0", diff --git a/cli/tauri.js/scripts/rustup-init.exe b/cli/tauri.js/scripts/rustup-init.exe new file mode 100644 index 000000000..cb1a6c290 Binary files /dev/null and b/cli/tauri.js/scripts/rustup-init.exe differ diff --git a/cli/tauri.js/scripts/rustup-init.sh b/cli/tauri.js/scripts/rustup-init.sh new file mode 100644 index 000000000..5b7c9d27b --- /dev/null +++ b/cli/tauri.js/scripts/rustup-init.sh @@ -0,0 +1,553 @@ +#!/bin/sh +# shellcheck shell=dash + +# This is just a little script that can be downloaded from the internet to +# install rustup. It just does platform detection, downloads the installer +# and runs it. + +# It runs on Unix shells like {a,ba,da,k,z}sh. It uses the common `local` +# extension. Note: Most shells limit `local` to 1 var per line, contra bash. + +set -u + +# If RUSTUP_UPDATE_ROOT is unset or empty, default it. +RUSTUP_UPDATE_ROOT="${RUSTUP_UPDATE_ROOT:-https://static.rust-lang.org/rustup}" + +#XXX: If you change anything here, please make the same changes in setup_mode.rs +usage() { + cat 1>&2 < Choose a default host triple + --default-toolchain Choose a default toolchain to install + --default-toolchain none Do not install any toolchains + --profile [minimal|default|complete] Choose a profile + -c, --component ... Component name to also install + -t, --target ... Target name to also install +EOF +} + +main() { + downloader --check + need_cmd uname + need_cmd mktemp + need_cmd chmod + need_cmd mkdir + need_cmd rm + need_cmd rmdir + + get_architecture || return 1 + local _arch="$RETVAL" + assert_nz "$_arch" "arch" + + local _ext="" + case "$_arch" in + *windows*) + _ext=".exe" + ;; + esac + + local _url="${RUSTUP_UPDATE_ROOT}/dist/${_arch}/rustup-init${_ext}" + + local _dir + _dir="$(mktemp -d 2>/dev/null || ensure mktemp -d -t rustup)" + local _file="${_dir}/rustup-init${_ext}" + + local _ansi_escapes_are_valid=false + if [ -t 2 ]; then + if [ "${TERM+set}" = 'set' ]; then + case "$TERM" in + xterm*|rxvt*|urxvt*|linux*|vt*) + _ansi_escapes_are_valid=true + ;; + esac + fi + fi + + # check if we have to use /dev/tty to prompt the user + local need_tty=yes + for arg in "$@"; do + case "$arg" in + -h|--help) + usage + exit 0 + ;; + -y) + # user wants to skip the prompt -- we don't need /dev/tty + need_tty=no + ;; + *) + ;; + esac + done + + if $_ansi_escapes_are_valid; then + printf "\33[1minfo:\33[0m downloading installer\n" 1>&2 + else + printf '%s\n' 'info: downloading installer' 1>&2 + fi + + ensure mkdir -p "$_dir" + ensure downloader "$_url" "$_file" "$_arch" + ensure chmod u+x "$_file" + if [ ! -x "$_file" ]; then + printf '%s\n' "Cannot execute $_file (likely because of mounting /tmp as noexec)." 1>&2 + printf '%s\n' "Please copy the file to a location where you can execute binaries and run ./rustup-init${_ext}." 1>&2 + exit 1 + fi + + if [ "$need_tty" = "yes" ]; then + # The installer is going to want to ask for confirmation by + # reading stdin. This script was piped into `sh` though and + # doesn't have stdin to pass to its children. Instead we're going + # to explicitly connect /dev/tty to the installer's stdin. + if [ ! -t 1 ]; then + err "Unable to run interactively. Run with -y to accept defaults, --help for additional options" + fi + + ignore "$_file" "$@" < /dev/tty + else + ignore "$_file" "$@" + fi + + local _retval=$? + + ignore rm "$_file" + ignore rmdir "$_dir" + + return "$_retval" +} + +get_bitness() { + need_cmd head + # Architecture detection without dependencies beyond coreutils. + # ELF files start out "\x7fELF", and the following byte is + # 0x01 for 32-bit and + # 0x02 for 64-bit. + # The printf builtin on some shells like dash only supports octal + # escape sequences, so we use those. + local _current_exe_head + _current_exe_head=$(head -c 5 /proc/self/exe ) + if [ "$_current_exe_head" = "$(printf '\177ELF\001')" ]; then + echo 32 + elif [ "$_current_exe_head" = "$(printf '\177ELF\002')" ]; then + echo 64 + else + err "unknown platform bitness" + fi +} + +get_endianness() { + local cputype=$1 + local suffix_eb=$2 + local suffix_el=$3 + + # detect endianness without od/hexdump, like get_bitness() does. + need_cmd head + need_cmd tail + + local _current_exe_endianness + _current_exe_endianness="$(head -c 6 /proc/self/exe | tail -c 1)" + if [ "$_current_exe_endianness" = "$(printf '\001')" ]; then + echo "${cputype}${suffix_el}" + elif [ "$_current_exe_endianness" = "$(printf '\002')" ]; then + echo "${cputype}${suffix_eb}" + else + err "unknown platform endianness" + fi +} + +get_architecture() { + local _ostype _cputype _bitness _arch _clibtype + _ostype="$(uname -s)" + _cputype="$(uname -m)" + _clibtype="gnu" + + if [ "$_ostype" = Linux ]; then + if [ "$(uname -o)" = Android ]; then + _ostype=Android + fi + if ldd --version 2>&1 | grep -q 'musl'; then + _clibtype="musl" + fi + fi + + if [ "$_ostype" = Darwin ] && [ "$_cputype" = i386 ]; then + # Darwin `uname -m` lies + if sysctl hw.optional.x86_64 | grep -q ': 1'; then + _cputype=x86_64 + fi + fi + + case "$_ostype" in + + Android) + _ostype=linux-android + ;; + + Linux) + _ostype=unknown-linux-$_clibtype + _bitness=$(get_bitness) + ;; + + FreeBSD) + _ostype=unknown-freebsd + ;; + + NetBSD) + _ostype=unknown-netbsd + ;; + + DragonFly) + _ostype=unknown-dragonfly + ;; + + Darwin) + _ostype=apple-darwin + ;; + + MINGW* | MSYS* | CYGWIN*) + _ostype=pc-windows-gnu + ;; + + *) + err "unrecognized OS type: $_ostype" + ;; + + esac + + case "$_cputype" in + + i386 | i486 | i686 | i786 | x86) + _cputype=i686 + ;; + + xscale | arm) + _cputype=arm + if [ "$_ostype" = "linux-android" ]; then + _ostype=linux-androideabi + fi + ;; + + armv6l) + _cputype=arm + if [ "$_ostype" = "linux-android" ]; then + _ostype=linux-androideabi + else + _ostype="${_ostype}eabihf" + fi + ;; + + armv7l | armv8l) + _cputype=armv7 + if [ "$_ostype" = "linux-android" ]; then + _ostype=linux-androideabi + else + _ostype="${_ostype}eabihf" + fi + ;; + + aarch64) + _cputype=aarch64 + ;; + + x86_64 | x86-64 | x64 | amd64) + _cputype=x86_64 + ;; + + mips) + _cputype=$(get_endianness mips '' el) + ;; + + mips64) + if [ "$_bitness" -eq 64 ]; then + # only n64 ABI is supported for now + _ostype="${_ostype}abi64" + _cputype=$(get_endianness mips64 '' el) + fi + ;; + + ppc) + _cputype=powerpc + ;; + + ppc64) + _cputype=powerpc64 + ;; + + ppc64le) + _cputype=powerpc64le + ;; + + s390x) + _cputype=s390x + ;; + riscv64) + _cputype=riscv64gc + ;; + *) + err "unknown CPU type: $_cputype" + + esac + + # Detect 64-bit linux with 32-bit userland + if [ "${_ostype}" = unknown-linux-gnu ] && [ "${_bitness}" -eq 32 ]; then + case $_cputype in + x86_64) + _cputype=i686 + ;; + mips64) + _cputype=$(get_endianness mips '' el) + ;; + powerpc64) + _cputype=powerpc + ;; + aarch64) + _cputype=armv7 + if [ "$_ostype" = "linux-android" ]; then + _ostype=linux-androideabi + else + _ostype="${_ostype}eabihf" + fi + ;; + riscv64gc) + err "riscv64 with 32-bit userland unsupported" + ;; + esac + fi + + # Detect armv7 but without the CPU features Rust needs in that build, + # and fall back to arm. + # See https://github.com/rust-lang/rustup.rs/issues/587. + if [ "$_ostype" = "unknown-linux-gnueabihf" ] && [ "$_cputype" = armv7 ]; then + if ensure grep '^Features' /proc/cpuinfo | grep -q -v neon; then + # At least one processor does not have NEON. + _cputype=arm + fi + fi + + _arch="${_cputype}-${_ostype}" + + RETVAL="$_arch" +} + +say() { + printf 'rustup: %s\n' "$1" +} + +err() { + say "$1" >&2 + exit 1 +} + +need_cmd() { + if ! check_cmd "$1"; then + err "need '$1' (command not found)" + fi +} + +check_cmd() { + command -v "$1" > /dev/null 2>&1 +} + +assert_nz() { + if [ -z "$1" ]; then err "assert_nz $2"; fi +} + +# Run a command that should never fail. If the command fails execution +# will immediately terminate with an error showing the failing +# command. +ensure() { + if ! "$@"; then err "command failed: $*"; fi +} + +# This is just for indicating that commands' results are being +# intentionally ignored. Usually, because it's being executed +# as part of error handling. +ignore() { + "$@" +} + +# This wraps curl or wget. Try curl first, if not installed, +# use wget instead. +downloader() { + local _dld + local _ciphersuites + if check_cmd curl; then + _dld=curl + elif check_cmd wget; then + _dld=wget + else + _dld='curl or wget' # to be used in error message of need_cmd + fi + + if [ "$1" = --check ]; then + need_cmd "$_dld" + elif [ "$_dld" = curl ]; then + get_ciphersuites_for_curl + _ciphersuites="$RETVAL" + if [ -n "$_ciphersuites" ]; then + curl --proto '=https' --tlsv1.2 --ciphers "$_ciphersuites" --silent --show-error --fail --location "$1" --output "$2" + else + echo "Warning: Not enforcing strong cipher suites for TLS, this is potentially less secure" + if ! check_help_for "$3" curl --proto --tlsv1.2; then + echo "Warning: Not enforcing TLS v1.2, this is potentially less secure" + curl --silent --show-error --fail --location "$1" --output "$2" + else + curl --proto '=https' --tlsv1.2 --silent --show-error --fail --location "$1" --output "$2" + fi + fi + elif [ "$_dld" = wget ]; then + get_ciphersuites_for_wget + _ciphersuites="$RETVAL" + if [ -n "$_ciphersuites" ]; then + wget --https-only --secure-protocol=TLSv1_2 --ciphers "$_ciphersuites" "$1" -O "$2" + else + echo "Warning: Not enforcing strong cipher suites for TLS, this is potentially less secure" + if ! check_help_for "$3" wget --https-only --secure-protocol; then + echo "Warning: Not enforcing TLS v1.2, this is potentially less secure" + wget "$1" -O "$2" + else + wget --https-only --secure-protocol=TLSv1_2 "$1" -O "$2" + fi + fi + else + err "Unknown downloader" # should not reach here + fi +} + +check_help_for() { + local _arch + local _cmd + local _arg + _arch="$1" + shift + _cmd="$1" + shift + + case "$_arch" in + + # If we're running on OS-X, older than 10.13, then we always + # fail to find these options to force fallback + *darwin*) + if check_cmd sw_vers; then + if [ "$(sw_vers -productVersion | cut -d. -f2)" -lt 13 ]; then + # Older than 10.13 + echo "Warning: Detected OS X platform older than 10.13" + return 1 + fi + fi + ;; + + esac + + for _arg in "$@"; do + if ! "$_cmd" --help | grep -q -- "$_arg"; then + return 1 + fi + done + + true # not strictly needed +} + +# Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites +# if support by local tools is detected. Detection currently supports these curl backends: +# GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty. +get_ciphersuites_for_curl() { + if [ -n "${RUSTUP_TLS_CIPHERSUITES-}" ]; then + # user specified custom cipher suites, assume they know what they're doing + RETVAL="$RUSTUP_TLS_CIPHERSUITES" + return + fi + + local _openssl_syntax="no" + local _gnutls_syntax="no" + local _backend_supported="yes" + if curl -V | grep -q ' OpenSSL/'; then + _openssl_syntax="yes" + elif curl -V | grep -iq ' LibreSSL/'; then + _openssl_syntax="yes" + elif curl -V | grep -iq ' BoringSSL/'; then + _openssl_syntax="yes" + elif curl -V | grep -iq ' GnuTLS/'; then + _gnutls_syntax="yes" + else + _backend_supported="no" + fi + + local _args_supported="no" + if [ "$_backend_supported" = "yes" ]; then + # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc. + if check_help_for "notspecified" "curl" "--tlsv1.2" "--ciphers" "--proto"; then + _args_supported="yes" + fi + fi + + local _cs="" + if [ "$_args_supported" = "yes" ]; then + if [ "$_openssl_syntax" = "yes" ]; then + _cs=$(get_strong_ciphersuites_for "openssl") + elif [ "$_gnutls_syntax" = "yes" ]; then + _cs=$(get_strong_ciphersuites_for "gnutls") + fi + fi + + RETVAL="$_cs" +} + +# Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites +# if support by local tools is detected. Detection currently supports these wget backends: +# GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty. +get_ciphersuites_for_wget() { + if [ -n "${RUSTUP_TLS_CIPHERSUITES-}" ]; then + # user specified custom cipher suites, assume they know what they're doing + RETVAL="$RUSTUP_TLS_CIPHERSUITES" + return + fi + + local _cs="" + if wget -V | grep -q '\-DHAVE_LIBSSL'; then + # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc. + if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then + _cs=$(get_strong_ciphersuites_for "openssl") + fi + elif wget -V | grep -q '\-DHAVE_LIBGNUTLS'; then + # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc. + if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then + _cs=$(get_strong_ciphersuites_for "gnutls") + fi + fi + + RETVAL="$_cs" +} + +# Return strong TLS 1.2-1.3 cipher suites in OpenSSL or GnuTLS syntax. TLS 1.2 +# excludes non-ECDHE and non-AEAD cipher suites. DHE is excluded due to bad +# DH params often found on servers (see RFC 7919). Sequence matches or is +# similar to Firefox 68 ESR with weak cipher suites disabled via about:config. +# $1 must be openssl or gnutls. +get_strong_ciphersuites_for() { + if [ "$1" = "openssl" ]; then + # OpenSSL is forgiving of unknown values, no problems with TLS 1.3 values on versions that don't support it yet. + echo "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384" + elif [ "$1" = "gnutls" ]; then + # GnuTLS isn't forgiving of unknown values, so this may require a GnuTLS version that supports TLS 1.3 even if wget doesn't. + # Begin with SECURE128 (and higher) then remove/add to build cipher suites. Produces same 9 cipher suites as OpenSSL but in slightly different order. + echo "SECURE128:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS-ALL:-CIPHER-ALL:-MAC-ALL:-KX-ALL:+AEAD:+ECDHE-ECDSA:+ECDHE-RSA:+AES-128-GCM:+CHACHA20-POLY1305:+AES-256-GCM" + fi +} + +main "$@" || exit 1 diff --git a/cli/tauri.js/src/api/dependency-manager/cargo-commands.ts b/cli/tauri.js/src/api/dependency-manager/cargo-commands.ts new file mode 100644 index 000000000..c08ec4825 --- /dev/null +++ b/cli/tauri.js/src/api/dependency-manager/cargo-commands.ts @@ -0,0 +1,61 @@ +import { ManagementType, Result } from './types' +import { getCrateLatestVersion, semverLt } from './util' +import getScriptVersion from '../../helpers/get-script-version' +import logger from '../../helpers/logger' +import { sync as spawnSync } from 'cross-spawn' +import inquirer from 'inquirer' + +const log = logger('dependency:cargo-commands') + +const dependencies = ['tauri-bundler'] + +async function manageDependencies(managementType: ManagementType): Promise { + const installedDeps = [] + const updatedDeps = [] + + for (const dependency of dependencies) { + const currentVersion = getScriptVersion('cargo', [dependency]) + if (currentVersion === null) { + log(`Installing ${dependency}...`) + spawnSync('cargo', ['install', dependency]) + installedDeps.push(dependency) + } else if (managementType === ManagementType.Update) { + const latestVersion = await getCrateLatestVersion(dependency) + if (semverLt(currentVersion, latestVersion)) { + const inquired = await inquirer.prompt([{ + type: 'confirm', + name: 'answer', + message: `[CARGO COMMANDS] "${dependency}" latest version is ${latestVersion}. Do you want to update?`, + default: false + }]) + if (inquired.answer) { + spawnSync('cargo', ['install', dependency, '--force']) + updatedDeps.push(dependency) + } + } else { + log(`"${dependency}" is up to date`) + } + } else { + log(`"${dependency}" is already installed`) + } + } + + const result: Result = new Map() + result.set(ManagementType.Install, installedDeps) + result.set(ManagementType.Update, updatedDeps) + + return result +} + +async function install(): Promise { + return await manageDependencies(ManagementType.Install) +} + +async function update(): Promise { + return await manageDependencies(ManagementType.Update) +} + +export { + install, + update +} diff --git a/cli/tauri.js/src/api/dependency-manager/cargo-crates.ts b/cli/tauri.js/src/api/dependency-manager/cargo-crates.ts new file mode 100644 index 000000000..c6d0e2e2f --- /dev/null +++ b/cli/tauri.js/src/api/dependency-manager/cargo-crates.ts @@ -0,0 +1,108 @@ +import { spawnSync } from './../../helpers/spawn' +import { CargoManifest, CargoManifestDependency, CargoLock } from './../../types/cargo' +import { ManagementType, Result } from './types' +import { getCrateLatestVersion, semverLt } from './util' +import logger from '../../helpers/logger' +import { resolve as appResolve, tauriDir } from '../../helpers/app-paths' +import { readFileSync, writeFileSync, existsSync } from 'fs' +import toml from '@tauri-apps/toml' +import inquirer from 'inquirer' + +const log = logger('dependency:crates') + +const dependencies = ['tauri'] + +function readToml(tomlPath: string): T | null { + if (existsSync(tomlPath)) { + const manifest = readFileSync(tomlPath).toString() + return toml.parse(manifest) as any as T + } + return null +} + +function dependencyDefinition(version: string): CargoManifestDependency { + return { version: version.substring(0, version.lastIndexOf('.')) } +} + +async function manageDependencies(managementType: ManagementType): Promise { + const installedDeps = [] + const updatedDeps = [] + const result: Result = new Map() + + const manifest = readToml(appResolve.tauri('Cargo.toml')) + + if (manifest === null) { + log('Cargo.toml not found. Skipping crates check...') + return result + } + + const lockPath = appResolve.tauri('Cargo.lock') + if (!existsSync(lockPath)) { + spawnSync('cargo', ['generate-lockfile'], tauriDir) + } + const lock = readToml(lockPath) + + for (const dependency of dependencies) { + const lockPackages = lock ? lock.package.filter(pkg => pkg.name === dependency) : [] + // eslint-disable-next-line security/detect-object-injection + const manifestDep = manifest.dependencies[dependency] + const currentVersion = lockPackages.length === 1 + ? lockPackages[0].version + : (typeof manifestDep === 'string' ? manifestDep : manifestDep?.version) + if (currentVersion === undefined) { + log(`Installing ${dependency}...`) + const latestVersion = await getCrateLatestVersion(dependency) + // eslint-disable-next-line security/detect-object-injection + manifest.dependencies[dependency] = dependencyDefinition(latestVersion) + installedDeps.push(dependency) + } else if (managementType === ManagementType.Update) { + const latestVersion = await getCrateLatestVersion(dependency) + if (semverLt(currentVersion, latestVersion)) { + const inquired = await inquirer.prompt([{ + type: 'confirm', + name: 'answer', + message: `[CRATES] "${dependency}" latest version is ${latestVersion}. Do you want to update?`, + default: false + }]) + if (inquired.answer) { + log(`Updating ${dependency}...`) + // eslint-disable-next-line security/detect-object-injection + manifest.dependencies[dependency] = dependencyDefinition(latestVersion) + updatedDeps.push(dependency) + } + } else { + log(`"${dependency}" is up to date`) + } + } else { + log(`"${dependency}" is already installed`) + } + } + + if (installedDeps.length || updatedDeps.length) { + writeFileSync(appResolve.tauri('Cargo.toml'), toml.stringify(manifest as any)) + } + if (updatedDeps.length) { + if (!existsSync(appResolve.tauri('Cargo.lock'))) { + spawnSync('cargo', ['generate-lockfile'], tauriDir) + } + spawnSync('cargo', ['update', ...updatedDeps.reduce((initialValue, dep) => [...initialValue, '-p', dep], [])], tauriDir) + } + + result.set(ManagementType.Install, installedDeps) + result.set(ManagementType.Update, updatedDeps) + + return result +} + +async function install(): Promise { + return await manageDependencies(ManagementType.Install) +} + +async function update(): Promise { + return await manageDependencies(ManagementType.Update) +} + +export { + install, + update +} diff --git a/cli/tauri.js/src/api/dependency-manager/index.ts b/cli/tauri.js/src/api/dependency-manager/index.ts new file mode 100644 index 000000000..be1a8d480 --- /dev/null +++ b/cli/tauri.js/src/api/dependency-manager/index.ts @@ -0,0 +1,24 @@ +import logger from '../../helpers/logger' +import * as rust from './rust' +import * as cargoCommands from './cargo-commands' +import * as cargoCrates from './cargo-crates' +import * as npmPackages from './npm-packages' + +const log = logger('dependency:manager') + +module.exports = { + async installDependencies() { + log('Installing missing dependencies...') + rust.install() + await cargoCommands.install() + await cargoCrates.install() + await npmPackages.install() + }, + async updateDependencies() { + log('Updating dependencies...') + rust.update() + await cargoCommands.update() + await cargoCrates.update() + await npmPackages.update() + } +} diff --git a/cli/tauri.js/src/api/dependency-manager/npm-packages.ts b/cli/tauri.js/src/api/dependency-manager/npm-packages.ts new file mode 100644 index 000000000..44eb4ea66 --- /dev/null +++ b/cli/tauri.js/src/api/dependency-manager/npm-packages.ts @@ -0,0 +1,60 @@ +import { ManagementType, Result } from './types' +import { getNpmLatestVersion, getNpmPackageVersion, installNpmPackage, updateNpmPackage, semverLt } from './util' +import logger from '../../helpers/logger' +import inquirer from 'inquirer' + +const log = logger('dependency:npm-packages') + +const dependencies = ['tauri'] + +async function manageDependencies(managementType: ManagementType): Promise { + const installedDeps = [] + const updatedDeps = [] + + for (const dependency of dependencies) { + const currentVersion = await getNpmPackageVersion(dependency) + if (currentVersion === null) { + log(`Installing ${dependency}...`) + installNpmPackage(dependency) + installedDeps.push(dependency) + } else if (managementType === ManagementType.Update) { + const latestVersion = getNpmLatestVersion(dependency) + if (semverLt(currentVersion, latestVersion)) { + const inquired = await inquirer.prompt([{ + type: 'confirm', + name: 'answer', + message: `[NPM]: "${dependency}" latest version is ${latestVersion}. Do you want to update?`, + default: false + }]) + if (inquired.answer) { + log(`Updating ${dependency}...`) + updateNpmPackage(dependency) + updatedDeps.push(dependency) + } + } else { + log(`"${dependency}" is up to date`) + } + } else { + log(`"${dependency}" is already installed`) + } + } + + const result: Result = new Map() + result.set(ManagementType.Install, installedDeps) + result.set(ManagementType.Update, updatedDeps) + + return result +} + +async function install(): Promise { + return await manageDependencies(ManagementType.Install) +} + +async function update(): Promise { + return await manageDependencies(ManagementType.Update) +} + +export { + install, + update +} diff --git a/cli/tauri.js/src/api/dependency-manager/rust.ts b/cli/tauri.js/src/api/dependency-manager/rust.ts new file mode 100644 index 000000000..8547bb27d --- /dev/null +++ b/cli/tauri.js/src/api/dependency-manager/rust.ts @@ -0,0 +1,57 @@ +import { ManagementType } from './types' +import { spawnSync } from '../../helpers/spawn' +import getScriptVersion from '../../helpers/get-script-version' +import logger from '../../helpers/logger' +import { createWriteStream, unlinkSync } from 'fs' +import { resolve } from 'path' +import { platform } from 'os' +import https from 'https' + +const log = logger('dependency:rust') + +async function download(url: string, dest: string): Promise { + const file = createWriteStream(dest) + return await new Promise((resolve, reject) => { + https.get(url, response => { + response.pipe(file) + file.on('finish', function() { + file.close() + resolve() + }) + }).on('error', function(err) { + unlinkSync(dest) + reject(err.message) + }) + }) +}; + +function installRustup(): void { + if (platform() === 'win32') { + return spawnSync('powershell', [resolve(__dirname, '../../scripts/rustup-init.exe')], process.cwd()) + } + return spawnSync('/bin/sh', [resolve(__dirname, '../../scripts/rustup-init.sh')], process.cwd()) +} + +function manageDependencies(managementType: ManagementType): void { + if (getScriptVersion('rustup') === null) { + log('Installing rustup...') + installRustup() + } + + if (managementType === ManagementType.Update) { + spawnSync('rustup', ['update'], process.cwd()) + } +} + +function install(): void { + return manageDependencies(ManagementType.Install) +} + +function update(): void { + return manageDependencies(ManagementType.Update) +} + +export { + install, + update +} diff --git a/cli/tauri.js/src/api/dependency-manager/types.ts b/cli/tauri.js/src/api/dependency-manager/types.ts new file mode 100644 index 000000000..7b1450b42 --- /dev/null +++ b/cli/tauri.js/src/api/dependency-manager/types.ts @@ -0,0 +1,6 @@ +export enum ManagementType { + Install, + Update +} + +export type Result = Map diff --git a/cli/tauri.js/src/api/dependency-manager/util.ts b/cli/tauri.js/src/api/dependency-manager/util.ts new file mode 100644 index 000000000..a090ce4cd --- /dev/null +++ b/cli/tauri.js/src/api/dependency-manager/util.ts @@ -0,0 +1,83 @@ +import https from 'https' +import { IncomingMessage } from 'http' +import { spawnSync } from '../../helpers/spawn' +import { sync as crossSpawnSync } from 'cross-spawn' +import { appDir, resolve as appResolve } from '../../helpers/app-paths' +import { existsSync } from 'fs' +import semver from 'semver' + +const BASE_URL = 'https://docs.rs/crate/' + +async function getCrateLatestVersion(crateName: string): Promise { + return await new Promise((resolve, reject) => { + const url = `${BASE_URL}${crateName}` + https.get(url, (res: IncomingMessage) => { + if (res.statusCode !== 302 || !res.headers.location) { + reject(res) + } else { + const version = res.headers.location.replace(url + '/', '') + resolve(version) + } + }) + }) +} + +function getNpmLatestVersion(packageName: string): string { + const child = crossSpawnSync('npm', ['show', packageName, 'version'], { cwd: appDir }) + return String(child.output[1]).replace('\n', '') +} + +async function getNpmPackageVersion(packageName: string): Promise { + return await new Promise((resolve) => { + const child = crossSpawnSync('npm', ['list', packageName, 'version', '--depth', '0'], { cwd: appDir }) + const output = String(child.output[1]) + // eslint-disable-next-line security/detect-non-literal-regexp + const matches = new RegExp(packageName + '@(\\S+)', 'g').exec(output) + if (matches?.[1]) { + resolve(matches[1]) + } else { + resolve(null) + } + }) +} + +function installNpmPackage(packageName: string): void { + const usesYarn = existsSync(appResolve.app('yarn.lock')) + if (usesYarn) { + spawnSync('yarn', ['add', packageName], appDir) + } else { + spawnSync('npm', ['install', packageName], appDir) + } +} + +function updateNpmPackage(packageName: string): void { + const usesYarn = existsSync(appResolve.app('yarn.lock')) + if (usesYarn) { + spawnSync('yarn', ['upgrade', packageName, '--latest'], appDir) + } else { + spawnSync('npm', ['install', `${packageName}@latest`], appDir) + } +} + +function padVersion(version: string): string { + let count = (version.match(/\./g) ?? []).length + while (count < 2) { + count++ + version += '.0' + } + return version +} + +function semverLt(first: string, second: string): boolean { + return semver.lt(padVersion(first), padVersion(second)) +} + +export { + getCrateLatestVersion, + getNpmLatestVersion, + getNpmPackageVersion, + installNpmPackage, + updateNpmPackage, + padVersion, + semverLt +} diff --git a/cli/tauri.js/src/api/info.ts b/cli/tauri.js/src/api/info.ts index 9bdb3d023..027e0c88c 100644 --- a/cli/tauri.js/src/api/info.ts +++ b/cli/tauri.js/src/api/info.ts @@ -10,6 +10,7 @@ import { TauriConfig } from './../types/config' import { CargoLock, CargoManifest } from '../types/cargo' import nonWebpackRequire from '../helpers/non-webpack-require' import { version } from '../../package.json' +import getScriptVersion from '../helpers/get-script-version' interface DirInfo { path: string @@ -45,17 +46,13 @@ function getVersion( args: string[] = [], formatter?: (output: string) => string ): string { - try { - const child = spawn(command, [...args, '--version']) - if (child.status === 0) { - const output = String(child.output[1]) - return chalk - .green(formatter === undefined ? output : formatter(output)) - .replace('\n', '') - } - return chalk.red('Not installed') - } catch (err) { + const version = getScriptVersion(command, args) + if (version === null) { return chalk.red('Not installed') + } else { + return chalk + .green(formatter === undefined ? version : formatter(version)) + .replace('\n', '') } } @@ -68,7 +65,7 @@ interface Info { function printInfo(info: Info): void { console.log( `${info.section ? '\n' : ''}${info.key}${ - info.value === undefined ? '' : ' - ' + info.value + info.value === undefined ? '' : ' - ' + info.value }` ) } @@ -173,7 +170,7 @@ function printAppInfo(tauriDir: string): void { ? chalk.green(config.build.devPath) : chalk.red('unset') }) - } catch (_) {} + } catch (_) { } } module.exports = () => { diff --git a/cli/tauri.js/src/helpers/get-script-version.ts b/cli/tauri.js/src/helpers/get-script-version.ts new file mode 100644 index 000000000..cd8cf3a3a --- /dev/null +++ b/cli/tauri.js/src/helpers/get-script-version.ts @@ -0,0 +1,17 @@ +import { sync as spawn } from 'cross-spawn' + +export default function getVersion( + command: string, + args: string[] = [] +): string | null { + try { + const child = spawn(command, [...args, '--version']) + if (child.status === 0) { + const output = String(child.output[1]) + return output + } + return null + } catch (err) { + return null + } +} diff --git a/cli/tauri.js/webpack.config.js b/cli/tauri.js/webpack.config.js index 838826c60..7cc1624bd 100644 --- a/cli/tauri.js/webpack.config.js +++ b/cli/tauri.js/webpack.config.js @@ -9,6 +9,7 @@ module.exports = { 'api/init': './src/api/init.ts', 'api/tauricon': './src/api/tauricon.ts', 'api/info': './src/api/info.ts', + 'api/dependency-manager': './src/api/dependency-manager/index.ts', 'helpers/tauri-config': './src/helpers/tauri-config.ts', 'helpers/spawn': './src/helpers/spawn.ts' }, diff --git a/cli/tauri.js/yarn.lock b/cli/tauri.js/yarn.lock index 6cfeead7c..34036e641 100644 --- a/cli/tauri.js/yarn.lock +++ b/cli/tauri.js/yarn.lock @@ -794,14 +794,6 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-typescript" "^7.10.4" -"@babel/runtime-corejs3@^7.8.3": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.10.4.tgz#f29fc1990307c4c57b10dbd6ce667b27159d9e0d" - integrity sha512-BFlgP2SoLO9HJX9WBwN67gHWMBhDX/eDz64Jajd6mR/UAUzqrNMm99d4qHnVaKscAElZoFiPv+JpR/Siud5lXw== - dependencies: - core-js-pure "^3.0.0" - regenerator-runtime "^0.13.4" - "@babel/runtime@^7.8.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.4.tgz#a6724f1a6b8d2f6ea5236dbfe58c7d7ea9c5eb99" @@ -1305,6 +1297,14 @@ dependencies: "@types/node" "*" +"@types/inquirer@^6.5.0": + version "6.5.0" + resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-6.5.0.tgz#b83b0bf30b88b8be7246d40e51d32fe9d10e09be" + integrity sha512-rjaYQ9b9y/VFGOpqBEXRavc3jh0a+e6evAbI31tMda8VlPaSy0AZJfXsvmIe3wklc7W6C3zCSfleuMXR7NOyXw== + dependencies: + "@types/through" "*" + rxjs "^6.4.0" + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" @@ -1365,9 +1365,9 @@ integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== "@types/node@*": - version "14.0.22" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.22.tgz#23ea4d88189cec7d58f9e6b66f786b215eb61bdc" - integrity sha512-emeGcJvdiZ4Z3ohbmw93E/64jRzUHAItSHt8nF7M4TGgQTiWqFVGB8KNpLGFmUHmHLvjvBgFwVlqNcq+VuGv9g== + version "14.0.23" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.23.tgz#676fa0883450ed9da0bb24156213636290892806" + integrity sha512-Z4U8yDAl5TFkmYsZdFPdjeMa57NOvnaf1tljHzhouaPEp7LCj2JKkejpI1ODviIAQuW4CcQmxkQ77rnLsOOoKw== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -1401,6 +1401,13 @@ dependencies: "@types/node" "*" +"@types/semver@^7.3.1": + version "7.3.1" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.1.tgz#7a9a5d595b6d873f338c867dcef64df289468cfa" + integrity sha512-ooD/FJ8EuwlDKOI6D9HWxgIgJjMg2cuziXm/42npDC8y4NjxplBUn9loewZiBNCt44450lHAU0OSb51/UqXeag== + dependencies: + "@types/node" "*" + "@types/sharp@0.25.0": version "0.25.0" resolved "https://registry.yarnpkg.com/@types/sharp/-/sharp-0.25.0.tgz#e21501779b110e26f33cec7fb1969e49e0cdc3c0" @@ -1423,6 +1430,13 @@ resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.6.tgz#a9ca4b70a18b270ccb2bc0aaafefd1d486b7ea74" integrity sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA== +"@types/through@*": + version "0.0.30" + resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895" + integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg== + dependencies: + "@types/node" "*" + "@types/tough-cookie@*": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d" @@ -1498,7 +1512,18 @@ eslint-scope "^5.0.0" eslint-utils "^2.0.0" -"@typescript-eslint/parser@3.6.0", "@typescript-eslint/parser@^3.0.1": +"@typescript-eslint/experimental-utils@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.6.1.tgz#b5a2738ebbceb3fa90c5b07d50bb1225403c4a54" + integrity sha512-oS+hihzQE5M84ewXrTlVx7eTgc52eu+sVmG7ayLfOhyZmJ8Unvf3osyFQNADHP26yoThFfbxcibbO0d2FjnYhg== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/types" "3.6.1" + "@typescript-eslint/typescript-estree" "3.6.1" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + +"@typescript-eslint/parser@3.6.0": version "3.6.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.6.0.tgz#79b5232e1a2d06f1fc745942b690cd87aca7b60e" integrity sha512-taghDxuLhbDAD1U5Fk8vF+MnR0yiFE9Z3v2/bYScFb0N1I9SK8eKHkdJl1DAD48OGFDMFTeOTX0z7g0W6SYUXw== @@ -1509,11 +1534,27 @@ "@typescript-eslint/typescript-estree" "3.6.0" eslint-visitor-keys "^1.1.0" +"@typescript-eslint/parser@^3.0.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.6.1.tgz#216e8adf4ee9c629f77c985476a2ea07fb80e1dc" + integrity sha512-SLihQU8RMe77YJ/jGTqOt0lMq7k3hlPVfp7v/cxMnXA9T0bQYoMDfTsNgHXpwSJM1Iq2aAJ8WqekxUwGv5F67Q== + dependencies: + "@types/eslint-visitor-keys" "^1.0.0" + "@typescript-eslint/experimental-utils" "3.6.1" + "@typescript-eslint/types" "3.6.1" + "@typescript-eslint/typescript-estree" "3.6.1" + eslint-visitor-keys "^1.1.0" + "@typescript-eslint/types@3.6.0": version "3.6.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.6.0.tgz#4bd6eee55d2f9d35a4b36c4804be1880bf68f7bc" integrity sha512-JwVj74ohUSt0ZPG+LZ7hb95fW8DFOqBuR6gE7qzq55KDI3BepqsCtHfBIoa0+Xi1AI7fq5nCu2VQL8z4eYftqg== +"@typescript-eslint/types@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.6.1.tgz#87600fe79a1874235d3cc1cf5c7e1a12eea69eee" + integrity sha512-NPxd5yXG63gx57WDTW1rp0cF3XlNuuFFB5G+Kc48zZ+51ZnQn9yjDEsjTPQ+aWM+V+Z0I4kuTFKjKvgcT1F7xQ== + "@typescript-eslint/typescript-estree@3.6.0": version "3.6.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.6.0.tgz#9b4cab43f1192b64ff51530815b8919f166ce177" @@ -1528,6 +1569,20 @@ semver "^7.3.2" tsutils "^3.17.1" +"@typescript-eslint/typescript-estree@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.6.1.tgz#a5c91fcc5497cce7922ff86bc37d5e5891dcdefa" + integrity sha512-G4XRe/ZbCZkL1fy09DPN3U0mR6SayIv1zSeBNquRFRk7CnVLgkC2ZPj8llEMJg5Y8dJ3T76SvTGtceytniaztQ== + dependencies: + "@typescript-eslint/types" "3.6.1" + "@typescript-eslint/visitor-keys" "3.6.1" + debug "^4.1.1" + glob "^7.1.6" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" + "@typescript-eslint/visitor-keys@3.6.0": version "3.6.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.6.0.tgz#44185eb0cc47651034faa95c5e2e8b314ecebb26" @@ -1535,6 +1590,13 @@ dependencies: eslint-visitor-keys "^1.1.0" +"@typescript-eslint/visitor-keys@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.6.1.tgz#5c57a7772f4dd623cfeacc219303e7d46f963b37" + integrity sha512-qC8Olwz5ZyMTZrh4Wl3K4U6tfms0R/mzU4/5W3XeUZptVraGVmbptJbn6h2Ey6Rb3hOs3zWoAUebZk8t47KGiQ== + dependencies: + eslint-visitor-keys "^1.1.0" + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -2418,16 +2480,16 @@ cacache@^12.0.2: y18n "^4.0.0" cacache@^15.0.4: - version "15.0.4" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.4.tgz#b2c23cf4ac4f5ead004fb15a0efb0a20340741f1" - integrity sha512-YlnKQqTbD/6iyoJvEY3KJftjrdBYroCbxxYXzhOzsFLWlp6KX4BOlEf4mTx0cMUfVaTS3ENL2QtDWeRYoGLkkw== + version "15.0.5" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0" + integrity sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A== dependencies: "@npmcli/move-file" "^1.0.1" chownr "^2.0.0" fs-minipass "^2.0.0" glob "^7.1.4" infer-owner "^1.0.4" - lru-cache "^5.1.1" + lru-cache "^6.0.0" minipass "^3.1.1" minipass-collect "^1.0.2" minipass-flush "^1.0.5" @@ -2510,9 +2572,9 @@ camelcase@^6.0.0: integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w== caniuse-lite@^1.0.30001093: - version "1.0.30001097" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001097.tgz#1129c40c9f5ee3282158da08fd915d301f4a9bd8" - integrity sha512-TeuSleKt/vWXaPkLVFqGDnbweYfq4IaZ6rUugFf3rWY6dlII8StUZ8Ddin0PkADfgYZ4wRqCdO2ORl4Rn5eZIA== + version "1.0.30001099" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001099.tgz#540118fcc6842d1fde62f4ee5521d1ec6afdb40e" + integrity sha512-sdS9A+sQTk7wKoeuZBN/YMAHVztUfVnjDi4/UV3sDE8xoh7YR12hKW+pIdB3oqKGwr9XaFL2ovfzt9w8eUI5CA== capture-exit@^2.0.0: version "2.0.0" @@ -2979,11 +3041,6 @@ core-js-compat@^3.6.2: browserslist "^4.8.5" semver "7.0.0" -core-js-pure@^3.0.0: - version "3.6.5" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813" - integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA== - core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -3204,13 +3261,6 @@ decamelize@^1.1.2, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= -decamelize@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-3.2.0.tgz#84b8e8f4f8c579f938e35e2cc7024907e0090851" - integrity sha512-4TgkVUsmmu7oCSyGBm5FvfMoACuoh9EOidm7V5/J2X2djAwwt57qb3F2KMP2ITqODTCSwb+YRV+0Zqrv18k/hw== - dependencies: - xregexp "^4.2.4" - decimal.js@^10.2.0: version "10.2.0" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.0.tgz#39466113a9e036111d02f82489b5fd6b0b5ed231" @@ -3555,9 +3605,9 @@ ecc-jsbn@~0.1.1: safer-buffer "^2.1.0" electron-to-chromium@^1.3.488: - version "1.3.496" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.496.tgz#3f43d32930481d82ad3663d79658e7c59a58af0b" - integrity sha512-TXY4mwoyowwi4Lsrq9vcTUYBThyc1b2hXaTZI13p8/FRhY2CTaq5lK+DVjhYkKiTLsKt569Xes+0J5JsVXFurQ== + version "1.3.497" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.497.tgz#de00f2f2f44c258c4577fbfbd5124b94c18bfa44" + integrity sha512-sPdW5bUDZwiFtoonuZCUwRGzsZmKzcLM0bMVhp6SMCfUG+B3faENLx3cE+o+K0Jl+MPuNA9s9cScyFjOlixZpQ== elliptic@^6.0.0, elliptic@^6.5.2: version "6.5.3" @@ -5091,7 +5141,7 @@ inquirer@7.3.0: cli-width "^3.0.0" external-editor "^3.0.3" figures "^3.0.0" - lodash "^4.17.15" + lodash "^4.17.16" mute-stream "0.0.8" run-async "^2.4.0" rxjs "^6.6.0" @@ -6357,7 +6407,7 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash@4.17.19, lodash@>=4.17.19, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: +lodash@4.17.19, lodash@>=4.17.19, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.16: version "4.17.19" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== @@ -6447,6 +6497,13 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + magic-string@^0.25.2: version "0.25.7" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" @@ -6818,9 +6875,9 @@ node-abi@^2.7.0: semver "^5.4.1" node-addon-api@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.0.0.tgz#812446a1001a54f71663bed188314bba07e09247" - integrity sha512-sSHCgWfJ+Lui/u+0msF3oyCgvdkhxDbkCS6Q8uiJquzOimkJBvX6hl5aSSA7DR1XbMpdM8r7phjcF63sF4rkKg== + version "3.0.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.0.1.tgz#990544a2607ec3f538443df4858f8c40089b7783" + integrity sha512-YUpjl57P55u2yUaKX5Bgy4t5s6SCNYMg+62XNg+k41aYbBL1NgWrZfcgljR5MxDxHDjzl0qHDNtH6SkW4DXNCA== node-int64@^0.4.0: version "0.4.0" @@ -7193,9 +7250,9 @@ p-limit@^2.0.0, p-limit@^2.2.0: p-try "^2.0.0" p-limit@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.1.tgz#584784ac0722d1aed09f19f90ed2999af6ce2839" - integrity sha512-mw/p92EyOzl2MhauKodw54Rx5ZK4624rNfgNaBguFZkHzyUG9WsDzFF5/yQVEJinbJDdP4jEfMN+uBquiGnaLg== + version "3.0.2" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.2.tgz#1664e010af3cadc681baafd3e2a437be7b0fb5fe" + integrity sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg== dependencies: p-try "^2.0.0" @@ -7945,9 +8002,9 @@ regexpu-core@^4.7.0: unicode-match-property-value-ecmascript "^1.2.0" registry-auth-token@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.1.1.tgz#40a33be1e82539460f94328b0f7f0f84c16d9479" - integrity sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA== + version "4.2.0" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.0.tgz#1d37dffda72bbecd0f581e4715540213a65eb7da" + integrity sha512-P+lWzPrsgfN+UEpDS3U8AQKg/UjZX6mQSJueZj3EK+vNESoqBSpBUD3gmu4sF9lOsjXWjF11dQKUqemf3veq1w== dependencies: rc "^1.2.8" @@ -8219,7 +8276,7 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.5.5, rxjs@^6.6.0: +rxjs@^6.4.0, rxjs@^6.5.5, rxjs@^6.6.0: version "6.6.0" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.0.tgz#af2901eedf02e3a83ffa7f886240ff9018bbec84" integrity sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg== @@ -8904,9 +8961,9 @@ strip-indent@^1.0.1: get-stdin "^4.0.1" strip-json-comments@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180" - integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w== + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== strip-json-comments@~2.0.1: version "2.0.1" @@ -9944,13 +10001,6 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xregexp@^4.2.4: - version "4.3.0" - resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.3.0.tgz#7e92e73d9174a99a59743f67a4ce879a04b5ae50" - integrity sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g== - dependencies: - "@babel/runtime-corejs3" "^7.8.3" - xtend@^4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" @@ -10014,12 +10064,12 @@ yargs@^13.2.4, yargs@^13.3.2: yargs-parser "^13.1.2" yargs@^15.0.2, yargs@^15.3.1: - version "15.4.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.0.tgz#53949fb768309bac1843de9b17b80051e9805ec2" - integrity sha512-D3fRFnZwLWp8jVAAhPZBsmeIHY8tTsb8ItV9KaAaopmC6wde2u6Yw29JBIZHXw14kgkRnYmDgmQU4FVMDlIsWw== + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== dependencies: cliui "^6.0.0" - decamelize "^3.2.0" + decamelize "^1.2.0" find-up "^4.1.0" get-caller-file "^2.0.1" require-directory "^2.1.1"