Browse Source

Merge branch 'master' into feat/support-install-latest

remotes/origin/feat/support-install-latest
nzhl 2 years ago committed by GitHub
parent
commit
5191c41ac6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      .changeset/red-flowers-collect.md
  2. 5
      .changeset/soft-laws-doubt.md
  3. 23
      .ci/install.sh
  4. 75
      .ci/prepare-version.js
  5. 91
      .ci/print-command-docs.js
  6. 62
      .github/workflows/debug.yml
  7. 8
      .github/workflows/installation_script.yml
  8. 4
      .github/workflows/release.yml
  9. 56
      .github/workflows/rust.yml
  10. 2
      .node-version
  11. 14
      CHANGELOG.md
  12. 221
      Cargo.lock
  13. 23
      Cargo.toml
  14. 4
      README.md
  15. 5
      docs/commands.md
  16. 2
      docs/fnm.svg
  17. 18
      e2e/__snapshots__/env.test.ts.snap
  18. 12
      e2e/aliases.test.ts
  19. 10
      e2e/basic.test.ts
  20. 6
      e2e/current.test.ts
  21. 4
      e2e/describe.ts
  22. 34
      e2e/env.test.ts
  23. 8
      e2e/exec.test.ts
  24. 8
      e2e/existing-installation.test.ts
  25. 6
      e2e/latest-lts.test.ts
  26. 8
      e2e/log-level.test.ts
  27. 8
      e2e/multishell.test.ts
  28. 8
      e2e/nvmrc-lts.test.ts
  29. 53
      e2e/shellcode/script.ts
  30. 27
      e2e/shellcode/shells.ts
  31. 2
      e2e/shellcode/shells/cmdCall.ts
  32. 2
      e2e/shellcode/shells/cmdEnv.ts
  33. 4
      e2e/shellcode/shells/expect-command-output.ts
  34. 2
      e2e/shellcode/shells/output-contains.ts
  35. 7
      e2e/shellcode/shells/redirect-output.ts
  36. 2
      e2e/shellcode/shells/sub-shell.ts
  37. 2
      e2e/shellcode/shells/types.ts
  38. 2
      e2e/shellcode/test-bin-dir.ts
  39. 2
      e2e/shellcode/test-cwd.ts
  40. 6
      e2e/shellcode/test-node-version.ts
  41. 3
      e2e/shellcode/test-tmp-dir.ts
  42. 52
      e2e/system-node.test.ts
  43. 6
      e2e/uninstall.test.ts
  44. 22
      jest.config.cjs
  45. 29
      package.json
  46. 477
      pnpm-lock.yaml
  47. 76
      src/commands/env.rs
  48. 4
      tsconfig.json

5
.changeset/red-flowers-collect.md

@ -1,5 +0,0 @@
---
"fnm": patch
---
This updates the Changesets configurations.

5
.changeset/soft-laws-doubt.md

@ -1,5 +0,0 @@
---
"fnm": patch
---
fix test: Use correct PATH for npm install test

23
.ci/install.sh

@ -2,10 +2,19 @@
set -e set -e
INSTALL_DIR="$HOME/.fnm"
RELEASE="latest" RELEASE="latest"
OS="$(uname -s)" OS="$(uname -s)"
if [ -d "$HOME/.fnm" ]; then
INSTALL_DIR="$HOME/.fnm"
elif [ -n "$XDG_DATA_HOME" ]; then
INSTALL_DIR="$XDG_DATA_HOME/fnm"
elif [ "$OS" = "Darwin" ]; then
INSTALL_DIR="$HOME/Library/Application Support/fnm"
else
INSTALL_DIR="$HOME/.local/share/fnm"
fi
# Parse Flags # Parse Flags
parse_args() { parse_args() {
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
@ -154,12 +163,12 @@ setup_shell() {
echo "Installing for Zsh. Appending the following to $CONF_FILE:" echo "Installing for Zsh. Appending the following to $CONF_FILE:"
echo "" echo ""
echo ' # fnm' echo ' # fnm'
echo ' export PATH='"$INSTALL_DIR"':$PATH' echo ' export PATH="'"$INSTALL_DIR"':$PATH"'
echo ' eval "`fnm env`"' echo ' eval "`fnm env`"'
echo '' >>$CONF_FILE echo '' >>$CONF_FILE
echo '# fnm' >>$CONF_FILE echo '# fnm' >>$CONF_FILE
echo 'export PATH='$INSTALL_DIR':$PATH' >>$CONF_FILE echo 'export PATH="'$INSTALL_DIR':$PATH"' >>$CONF_FILE
echo 'eval "`fnm env`"' >>$CONF_FILE echo 'eval "`fnm env`"' >>$CONF_FILE
elif [ "$CURRENT_SHELL" = "fish" ]; then elif [ "$CURRENT_SHELL" = "fish" ]; then
@ -168,11 +177,11 @@ setup_shell() {
echo "Installing for Fish. Appending the following to $CONF_FILE:" echo "Installing for Fish. Appending the following to $CONF_FILE:"
echo "" echo ""
echo ' # fnm' echo ' # fnm'
echo ' set PATH '"$INSTALL_DIR"' $PATH' echo ' set PATH "'"$INSTALL_DIR"'" $PATH'
echo ' fnm env | source' echo ' fnm env | source'
echo '# fnm' >>$CONF_FILE echo '# fnm' >>$CONF_FILE
echo 'set PATH '"$INSTALL_DIR"' $PATH' >>$CONF_FILE echo 'set PATH "'"$INSTALL_DIR"'" $PATH' >>$CONF_FILE
echo 'fnm env | source' >>$CONF_FILE echo 'fnm env | source' >>$CONF_FILE
elif [ "$CURRENT_SHELL" = "bash" ]; then elif [ "$CURRENT_SHELL" = "bash" ]; then
@ -185,12 +194,12 @@ setup_shell() {
echo "Installing for Bash. Appending the following to $CONF_FILE:" echo "Installing for Bash. Appending the following to $CONF_FILE:"
echo "" echo ""
echo ' # fnm' echo ' # fnm'
echo ' export PATH='"$INSTALL_DIR"':$PATH' echo ' export PATH="'"$INSTALL_DIR"':$PATH"'
echo ' eval "`fnm env`"' echo ' eval "`fnm env`"'
echo '' >>$CONF_FILE echo '' >>$CONF_FILE
echo '# fnm' >>$CONF_FILE echo '# fnm' >>$CONF_FILE
echo 'export PATH='"$INSTALL_DIR"':$PATH' >>$CONF_FILE echo 'export PATH="'"$INSTALL_DIR"':$PATH"' >>$CONF_FILE
echo 'eval "`fnm env`"' >>$CONF_FILE echo 'eval "`fnm env`"' >>$CONF_FILE
else else

75
.ci/prepare-version.js

@ -2,28 +2,27 @@
/// @ts-check /// @ts-check
const fs = require("fs"); import fs from "fs"
const cp = require("child_process"); import cp from "child_process"
const path = require("path"); import cmd from "cmd-ts"
const cmd = require("cmd-ts"); import toml from "toml"
const toml = require("toml"); import assert from "assert"
const assert = require("assert");
const CARGO_TOML_PATH = path.join(__dirname, "../Cargo.toml"); const CARGO_TOML_PATH = new URL("../Cargo.toml", import.meta.url).pathname
const command = cmd.command({ const command = cmd.command({
name: "prepare-version", name: "prepare-version",
description: "Prepare a new fnm version", description: "Prepare a new fnm version",
args: {}, args: {},
async handler({}) { async handler({}) {
updateCargoToml(await getPackageVersion()); updateCargoToml(await getPackageVersion())
exec("cargo build --release"); exec("cargo build --release")
exec("yarn generate-command-docs --binary-path=./target/release/fnm"); exec("yarn generate-command-docs --binary-path=./target/release/fnm")
exec("./.ci/record_screen.sh"); exec("./.ci/record_screen.sh")
}, },
}); })
cmd.run(cmd.binary(command), process.argv); cmd.run(cmd.binary(command), process.argv)
////////////////////// //////////////////////
// Helper functions // // Helper functions //
@ -34,56 +33,40 @@ cmd.run(cmd.binary(command), process.argv);
*/ */
async function getPackageVersion() { async function getPackageVersion() {
const pkgJson = await fs.promises.readFile( const pkgJson = await fs.promises.readFile(
path.join(__dirname, "../package.json"), new URL("../package.json", import.meta.url),
"utf8" "utf8"
); )
const version = JSON.parse(pkgJson).version; const version = JSON.parse(pkgJson).version
assert(version, "package.json version is not set"); assert(version, "package.json version is not set")
return version; return version
} }
function updateCargoToml(nextVersion) { function updateCargoToml(nextVersion) {
const cargoToml = fs.readFileSync(CARGO_TOML_PATH, "utf8"); const cargoToml = fs.readFileSync(CARGO_TOML_PATH, "utf8")
const cargoTomlContents = toml.parse(cargoToml); const cargoTomlContents = toml.parse(cargoToml)
const currentVersion = cargoTomlContents.package.version; const currentVersion = cargoTomlContents.package.version
const newToml = cargoToml.replace( const newToml = cargoToml.replace(
`version = "${currentVersion}"`, `version = "${currentVersion}"`,
`version = "${nextVersion}"` `version = "${nextVersion}"`
); )
if (newToml === cargoToml) { if (newToml === cargoToml) {
console.error("Cargo.toml didn't change, error!"); console.error("Cargo.toml didn't change, error!")
process.exitCode = 1; process.exitCode = 1
return; return
} }
fs.writeFileSync(CARGO_TOML_PATH, newToml, "utf8"); fs.writeFileSync(CARGO_TOML_PATH, newToml, "utf8")
return nextVersion; return nextVersion
} }
function exec(command, env) { function exec(command, env) {
console.log(`$ ${command}`); console.log(`$ ${command}`)
return cp.execSync(command, { return cp.execSync(command, {
cwd: path.join(__dirname, ".."), // root of repo cwd: new URL("..", import.meta.url),
stdio: "inherit", stdio: "inherit",
env: { ...process.env, ...env }, env: { ...process.env, ...env },
}); })
}
/**
* @param {"patch" | "minor" | "major"} type
* @param {string} version
*/
function changeVersion(type, version) {
const [major, minor, patch] = version.split(".").map((x) => parseInt(x, 10));
switch (type) {
case "patch":
return [major, minor, patch + 1].join(".");
case "minor":
return [major, minor + 1, 0].join(".");
case "major":
return [major + 1, 0, 0].join(".");
}
} }

91
.ci/print-command-docs.js

@ -2,24 +2,23 @@
/// @ts-check /// @ts-check
const execa = require("execa"); import { execa } from "execa"
const path = require("path"); import fs from "node:fs"
const fs = require("fs"); import cmd from "cmd-ts"
const cmd = require("cmd-ts"); import cmdFs from "cmd-ts/dist/cjs/batteries/fs.js"
const cmdFs = require("cmd-ts/dist/cjs/batteries/fs");
const FnmBinaryPath = { const FnmBinaryPath = {
...cmdFs.ExistingPath, ...cmdFs.ExistingPath,
defaultValue() { defaultValue() {
const target = path.join(__dirname, "../target/debug/fnm"); const target = new URL("../target/debug/fnm", import.meta.url)
if (!fs.existsSync(target)) { if (!fs.existsSync(target)) {
throw new Error( throw new Error(
"Can't find debug target, please run `cargo build` or provide a specific binary path" "Can't find debug target, please run `cargo build` or provide a specific binary path"
); )
} }
return target; return target.pathname
}, },
}; }
const command = cmd.command({ const command = cmd.command({
name: "print-command-docs", name: "print-command-docs",
@ -36,27 +35,27 @@ const command = cmd.command({
}), }),
}, },
async handler({ checkForDirty, fnmPath }) { async handler({ checkForDirty, fnmPath }) {
const targetFile = path.join(__dirname, "../docs/commands.md"); const targetFile = new URL("../docs/commands.md", import.meta.url).pathname
await main(targetFile, fnmPath); await main(targetFile, fnmPath)
if (checkForDirty) { if (checkForDirty) {
const gitStatus = await checkGitStatus(targetFile); const gitStatus = await checkGitStatus(targetFile)
if (gitStatus.state === "dirty") { if (gitStatus.state === "dirty") {
process.exitCode = 1; process.exitCode = 1
console.error( console.error(
"The file has changed. Please re-run `yarn generate-command-docs`." "The file has changed. Please re-run `yarn generate-command-docs`."
); )
console.error(`hint: The following diff was found:`); console.error(`hint: The following diff was found:`)
console.error(); console.error()
console.error(gitStatus.diff); console.error(gitStatus.diff)
} }
} }
}, },
}); })
cmd.run(cmd.binary(command), process.argv).catch((err) => { cmd.run(cmd.binary(command), process.argv).catch((err) => {
console.error(err); console.error(err)
process.exitCode = process.exitCode || 1; process.exitCode = process.exitCode || 1
}); })
/** /**
* @param {string} targetFile * @param {string} targetFile
@ -64,20 +63,20 @@ cmd.run(cmd.binary(command), process.argv).catch((err) => {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async function main(targetFile, fnmPath) { async function main(targetFile, fnmPath) {
const stream = fs.createWriteStream(targetFile); const stream = fs.createWriteStream(targetFile)
const { subcommands, text: mainText } = await getCommandHelp(fnmPath); const { subcommands, text: mainText } = await getCommandHelp(fnmPath)
await write(stream, line(`fnm`, mainText)); await write(stream, line(`fnm`, mainText))
for (const subcommand of subcommands) { for (const subcommand of subcommands) {
const { text: subcommandText } = await getCommandHelp(fnmPath, subcommand); const { text: subcommandText } = await getCommandHelp(fnmPath, subcommand)
await write(stream, "\n" + line(`fnm ${subcommand}`, subcommandText)); await write(stream, "\n" + line(`fnm ${subcommand}`, subcommandText))
} }
stream.close(); stream.close()
await execa(`yarn`, ["prettier", "--write", targetFile]); await execa(`yarn`, ["prettier", "--write", targetFile])
} }
/** /**
@ -87,14 +86,14 @@ async function main(targetFile, fnmPath) {
*/ */
function write(stream, content) { function write(stream, content) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
stream.write(content, (err) => (err ? reject(err) : resolve())); stream.write(content, (err) => (err ? reject(err) : resolve()))
}); })
} }
function line(cmd, text) { function line(cmd, text) {
const cmdCode = "`" + cmd + "`"; const cmdCode = "`" + cmd + "`"
const textCode = "```\n" + text + "\n```"; const textCode = "```\n" + text + "\n```"
return `# ${cmdCode}\n${textCode}`; return `# ${cmdCode}\n${textCode}`
} }
/** /**
@ -103,25 +102,25 @@ function line(cmd, text) {
* @returns {Promise<{ subcommands: string[], text: string }>} * @returns {Promise<{ subcommands: string[], text: string }>}
*/ */
async function getCommandHelp(fnmPath, command) { async function getCommandHelp(fnmPath, command) {
const cmdArg = command ? [command] : []; const cmdArg = command ? [command] : []
const result = await run(fnmPath, [...cmdArg, "--help"]); const result = await run(fnmPath, [...cmdArg, "--help"])
const text = result.stdout; const text = result.stdout
const rows = text.split("\n"); const rows = text.split("\n")
const headerIndex = rows.findIndex((x) => x.includes("SUBCOMMANDS")); const headerIndex = rows.findIndex((x) => x.includes("SUBCOMMANDS"))
/** @type {string[]} */ /** @type {string[]} */
const subcommands = []; const subcommands = []
if (!command) { if (!command) {
for (const row of rows.slice(headerIndex + 1)) { for (const row of rows.slice(headerIndex + 1)) {
const [, word] = row.split(/\s+/); const [, word] = row.split(/\s+/)
if (word && word[0].toLowerCase() === word[0]) { if (word && word[0].toLowerCase() === word[0]) {
subcommands.push(word); subcommands.push(word)
} }
} }
} }
return { return {
subcommands, subcommands,
text, text,
}; }
} }
/** /**
@ -133,7 +132,7 @@ function run(fnmPath, args) {
reject: false, reject: false,
stdout: "pipe", stdout: "pipe",
stderr: "pipe", stderr: "pipe",
}); })
} }
/** /**
@ -147,9 +146,9 @@ async function checkGitStatus(targetFile) {
{ {
reject: false, reject: false,
} }
); )
if (exitCode === 0) { if (exitCode === 0) {
return { state: "clean" }; return { state: "clean" }
} }
return { state: "dirty", diff: stdout }; return { state: "dirty", diff: stdout }
} }

62
.github/workflows/debug.yml

@ -0,0 +1,62 @@
name: "debug"
on:
workflow_dispatch:
concurrency:
group: debug
cancel-in-progress: true
jobs:
e2e_windows_debug:
runs-on: windows-latest
name: "e2e/windows/debug"
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.commit_hash }}
- name: Download artifact
id: download-artifact
uses: dawidd6/action-download-artifact@v2
with:
workflow: rust.yml
workflow_conclusion: ""
branch: ${{ env.GITHUB_REF }}
name: "fnm-windows"
path: "target/release"
if_no_artifact_found: ignore
- uses: hecrj/setup-rust-action@v1
if: steps.download-artifact.outputs.artifact-found == false
with:
rust-version: stable
- uses: Swatinem/rust-cache@v2
if: steps.download-artifact.outputs.artifact-found == false
- name: Build release binary
if: steps.download-artifact.outputs.artifact-found == false
run: cargo build --release
env:
RUSTFLAGS: "-C target-feature=+crt-static"
- uses: pnpm/action-setup@v2.2.4
with:
run_install: false
- uses: actions/setup-node@v3
with:
node-version: 16.x
cache: 'pnpm'
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- run: pnpm install
- name: 🐛 Debug Build
if: always()
uses: mxschmitt/action-tmate@v3
with:
limit-access-to-actor: true

8
.github/workflows/installation_script.yml

@ -36,16 +36,16 @@ jobs:
bash ./.ci/install.sh bash ./.ci/install.sh
echo "fnm --version" echo "fnm --version"
~/.fnm/fnm --version ~/.local/share/fnm/fnm --version
echo "eval fnm env" echo "eval fnm env"
eval "$(~/.fnm/fnm env)" eval "$(~/.local/share/fnm/fnm env)"
echo "fnm install" echo "fnm install"
~/.fnm/fnm install 12 ~/.local/share/fnm/fnm install 12
echo "node -v" echo "node -v"
~/.fnm/fnm exec --using=12 -- node -v ~/.local/share/fnm/fnm exec --using=12 -- node -v
' '
test_against_latest_release: test_against_latest_release:

4
.github/workflows/release.yml

@ -19,9 +19,9 @@ jobs:
with: with:
rust-version: stable rust-version: stable
- uses: Swatinem/rust-cache@v1 - uses: Swatinem/rust-cache@v2
- uses: pnpm/action-setup@v2.2.2 - uses: pnpm/action-setup@v2.2.4
with: with:
run_install: false run_install: false

56
.github/workflows/rust.yml

@ -7,7 +7,7 @@ on:
- master - master
concurrency: concurrency:
group: ${{ github.head_ref }} group: ci-${{ github.head_ref }}
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
@ -17,7 +17,7 @@ jobs:
- uses: hecrj/setup-rust-action@v1 - uses: hecrj/setup-rust-action@v1
with: with:
rust-version: stable rust-version: stable
- uses: Swatinem/rust-cache@v1 - uses: Swatinem/rust-cache@v2
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: cargo fmt - name: cargo fmt
run: cargo fmt -- --check run: cargo fmt -- --check
@ -28,7 +28,7 @@ jobs:
- uses: hecrj/setup-rust-action@v1 - uses: hecrj/setup-rust-action@v1
with: with:
rust-version: stable rust-version: stable
- uses: Swatinem/rust-cache@v1 - uses: Swatinem/rust-cache@v2
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: cargo clippy - name: cargo clippy
run: cargo clippy -- -D warnings run: cargo clippy -- -D warnings
@ -42,7 +42,7 @@ jobs:
- uses: hecrj/setup-rust-action@v1 - uses: hecrj/setup-rust-action@v1
with: with:
rust-version: stable rust-version: stable
- uses: Swatinem/rust-cache@v1 - uses: Swatinem/rust-cache@v2
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Run tests - name: Run tests
run: cargo test run: cargo test
@ -54,7 +54,7 @@ jobs:
- uses: hecrj/setup-rust-action@v1 - uses: hecrj/setup-rust-action@v1
with: with:
rust-version: stable rust-version: stable
- uses: Swatinem/rust-cache@v1 - uses: Swatinem/rust-cache@v2
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Build release binary - name: Build release binary
run: cargo build --release run: cargo build --release
@ -72,7 +72,7 @@ jobs:
- uses: hecrj/setup-rust-action@v1 - uses: hecrj/setup-rust-action@v1
with: with:
rust-version: stable rust-version: stable
- uses: Swatinem/rust-cache@v1 - uses: Swatinem/rust-cache@v2
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Build release binary - name: Build release binary
run: cargo build --release run: cargo build --release
@ -101,7 +101,7 @@ jobs:
path: target/release path: target/release
- name: mark binary as executable - name: mark binary as executable
run: chmod +x target/release/fnm run: chmod +x target/release/fnm
- uses: pnpm/action-setup@v2.2.2 - uses: pnpm/action-setup@v2.2.4
with: with:
run_install: false run_install: false
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
@ -135,7 +135,7 @@ jobs:
with: with:
name: fnm-windows name: fnm-windows
path: target/release path: target/release
- uses: pnpm/action-setup@v2.2.2 - uses: pnpm/action-setup@v2.2.4
with: with:
run_install: false run_install: false
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
@ -159,6 +159,40 @@ jobs:
FNM_TARGET_NAME: "release" FNM_TARGET_NAME: "release"
FORCE_COLOR: "1" FORCE_COLOR: "1"
# e2e_windows_debug:
# runs-on: windows-latest
# name: "e2e/windows/debug"
# environment: Debug
# needs: [e2e_windows]
# if: contains(join(needs.*.result, ','), 'failure')
# steps:
# - uses: actions/checkout@v3
# - uses: actions/download-artifact@v3
# with:
# name: fnm-windows
# path: target/release
# - uses: pnpm/action-setup@v2.2.2
# with:
# run_install: false
# - uses: actions/setup-node@v3
# with:
# node-version: 16.x
# cache: 'pnpm'
# - name: Get pnpm store directory
# id: pnpm-cache
# run: |
# echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
# - uses: actions/cache@v3
# name: Setup pnpm cache
# with:
# path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
# key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
# restore-keys: |
# ${{ runner.os }}-pnpm-store-
# - run: pnpm install
# - name: 🐛 Debug Build
# uses: mxschmitt/action-tmate@v3
e2e_linux: e2e_linux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build_static_linux_binary] needs: [build_static_linux_binary]
@ -173,7 +207,7 @@ jobs:
path: target/release path: target/release
- name: mark binary as executable - name: mark binary as executable
run: chmod +x target/release/fnm run: chmod +x target/release/fnm
- uses: pnpm/action-setup@v2.2.2 - uses: pnpm/action-setup@v2.2.4
with: with:
run_install: false run_install: false
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
@ -205,7 +239,7 @@ jobs:
with: with:
rust-version: stable rust-version: stable
targets: x86_64-unknown-linux-musl targets: x86_64-unknown-linux-musl
- uses: Swatinem/rust-cache@v1 - uses: Swatinem/rust-cache@v2
with: with:
key: static-linux-binary key: static-linux-binary
- name: Install musl tools - name: Install musl tools
@ -245,7 +279,7 @@ jobs:
- uses: hecrj/setup-rust-action@v1 - uses: hecrj/setup-rust-action@v1
with: with:
rust-version: stable rust-version: stable
- uses: Swatinem/rust-cache@v1 - uses: Swatinem/rust-cache@v2
with: with:
key: arm-binary-${{ matrix.arch }} key: arm-binary-${{ matrix.arch }}
- name: 'Download `cross` crate' - name: 'Download `cross` crate'

2
.node-version

@ -1 +1 @@
16.17.1 18.12.1

14
CHANGELOG.md

@ -1,5 +1,19 @@
## 1.31.0 (2022-02-16) ## 1.31.0 (2022-02-16)
## 1.32.0
### Minor Changes
- Add `--json` to `fnm env` to output the env vars as JSON ([#800](https://github.com/Schniz/fnm/pull/800))
### Patch Changes
- This updates the Changesets configurations. ([#783](https://github.com/Schniz/fnm/pull/783))
- fix test: Use correct PATH for npm install test ([#768](https://github.com/Schniz/fnm/pull/768))
- Make installation script respect `$XDG_DATA_HOME` ([#614](https://github.com/Schniz/fnm/pull/614))
## 1.31.1 ## 1.31.1
### Patch Changes ### Patch Changes

221
Cargo.lock generated

@ -55,9 +55,9 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.65" version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
[[package]] [[package]]
name = "async-compression" name = "async-compression"
@ -205,9 +205,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.22" version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
dependencies = [ dependencies = [
"iana-time-zone", "iana-time-zone",
"js-sys", "js-sys",
@ -230,9 +230,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "3.2.22" version = "3.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
dependencies = [ dependencies = [
"atty", "atty",
"bitflags", "bitflags",
@ -297,19 +297,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "console"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"terminal_size",
"winapi",
]
[[package]] [[package]]
name = "constant_time_eq" name = "constant_time_eq"
version = "0.1.5" version = "0.1.5"
@ -479,19 +466,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "dashmap"
version = "5.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
dependencies = [
"cfg-if",
"hashbrown",
"lock_api",
"once_cell",
"parking_lot_core",
]
[[package]] [[package]]
name = "diff" name = "diff"
version = "0.1.13" version = "0.1.13"
@ -549,9 +523,9 @@ checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]] [[package]]
name = "embed-resource" name = "embed-resource"
version = "1.7.3" version = "1.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936c1354206a875581696369aef920e12396e93bbd251c43a7a3f3fa85023a7d" checksum = "782e883d0b9a3db55e6edee9939305d0b5c2ee6df793966d84b31144873e5bde"
dependencies = [ dependencies = [
"cc", "cc",
"rustc_version", "rustc_version",
@ -560,12 +534,6 @@ dependencies = [
"winreg", "winreg",
] ]
[[package]]
name = "encode_unicode"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.31" version = "0.8.31"
@ -586,9 +554,9 @@ dependencies = [
[[package]] [[package]]
name = "env_logger" name = "env_logger"
version = "0.9.1" version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272" checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
dependencies = [ dependencies = [
"atty", "atty",
"humantime", "humantime",
@ -630,7 +598,7 @@ dependencies = [
[[package]] [[package]]
name = "fnm" name = "fnm"
version = "1.31.1" version = "1.32.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"atty", "atty",
@ -645,7 +613,6 @@ dependencies = [
"encoding_rs_io", "encoding_rs_io",
"env_logger", "env_logger",
"indoc", "indoc",
"insta",
"junction", "junction",
"log", "log",
"pretty_assertions", "pretty_assertions",
@ -653,8 +620,6 @@ dependencies = [
"semver", "semver",
"serde", "serde",
"serde_json", "serde_json",
"serial_test",
"shell-escape",
"sysinfo", "sysinfo",
"tar", "tar",
"tempfile", "tempfile",
@ -680,21 +645,6 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "futures"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]] [[package]]
name = "futures-channel" name = "futures-channel"
version = "0.3.24" version = "0.3.24"
@ -702,7 +652,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-sink",
] ]
[[package]] [[package]]
@ -711,17 +660,6 @@ version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf"
[[package]]
name = "futures-executor"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]] [[package]]
name = "futures-io" name = "futures-io"
version = "0.3.24" version = "0.3.24"
@ -746,10 +684,8 @@ version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90"
dependencies = [ dependencies = [
"futures-channel",
"futures-core", "futures-core",
"futures-io", "futures-io",
"futures-sink",
"futures-task", "futures-task",
"memchr", "memchr",
"pin-project-lite", "pin-project-lite",
@ -954,19 +890,6 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3" checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3"
[[package]]
name = "insta"
version = "1.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581d4e3314cae4536e5d22ffd23189d4a374696c5ef733eadafae0ed273fd303"
dependencies = [
"console",
"lazy_static",
"linked-hash-map",
"similar",
"yaml-rust",
]
[[package]] [[package]]
name = "instant" name = "instant"
version = "0.1.12" version = "0.1.12"
@ -1043,22 +966,6 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "lock_api"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.17" version = "0.4.17"
@ -1211,29 +1118,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
]
[[package]] [[package]]
name = "password-hash" name = "password-hash"
version = "0.4.2" version = "0.4.2"
@ -1419,9 +1303,9 @@ dependencies = [
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.11.12" version = "0.11.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c"
dependencies = [ dependencies = [
"async-compression", "async-compression",
"base64", "base64",
@ -1585,18 +1469,18 @@ checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.145" version = "1.0.147"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.145" version = "1.0.147"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1605,9 +1489,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.86" version = "1.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074" checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
dependencies = [ dependencies = [
"itoa 1.0.4", "itoa 1.0.4",
"ryu", "ryu",
@ -1626,32 +1510,6 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serial_test"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92761393ee4dc3ff8f4af487bd58f4307c9329bbedea02cac0089ad9c411e153"
dependencies = [
"dashmap",
"futures",
"lazy_static",
"log",
"parking_lot",
"serial_test_derive",
]
[[package]]
name = "serial_test_derive"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b6f5d1c3087fb119617cff2966fe3808a80e5eb59a8c1601d5994d66f4346a5"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "sha1" name = "sha1"
version = "0.10.5" version = "0.10.5"
@ -1684,18 +1542,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "shell-escape"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
[[package]]
name = "similar"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62ac7f900db32bf3fd12e0117dd3dc4da74bc52ebaac97f39668446d89694803"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.7" version = "0.4.7"
@ -1705,12 +1551,6 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "smallvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.4.7" version = "0.4.7"
@ -1799,16 +1639,6 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "terminal_size"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
dependencies = [
"libc",
"winapi",
]
[[package]] [[package]]
name = "test-log" name = "test-log"
version = "0.2.11" version = "0.2.11"
@ -1822,9 +1652,9 @@ dependencies = [
[[package]] [[package]]
name = "textwrap" name = "textwrap"
version = "0.15.1" version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]] [[package]]
name = "thiserror" name = "thiserror"
@ -2267,15 +2097,6 @@ dependencies = [
"lzma-sys", "lzma-sys",
] ]
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
[[package]] [[package]]
name = "yansi" name = "yansi"
version = "0.5.1" version = "0.5.1"

23
Cargo.toml

@ -1,6 +1,6 @@
[package] [package]
name = "fnm" name = "fnm"
version = "1.31.1" version = "1.32.0"
authors = ["Gal Schlezinger <gal@spitfire.co.il>"] authors = ["Gal Schlezinger <gal@spitfire.co.il>"]
edition = "2021" edition = "2021"
build = "build.rs" build = "build.rs"
@ -9,39 +9,36 @@ repository = "https://github.com/Schniz/fnm"
description = "Fast and simple Node.js version manager" description = "Fast and simple Node.js version manager"
[dependencies] [dependencies]
serde = { version = "1.0.145", features = ["derive"] } serde = { version = "1.0.147", features = ["derive"] }
clap = { version = "3.2.22", features = ["derive", "env"] } clap = { version = "3.2.23", features = ["derive", "env"] }
serde_json = "1.0.85" serde_json = "1.0.87"
chrono = { version = "0.4.22", features = ["serde"] } chrono = { version = "0.4.23", features = ["serde"] }
tar = "0.4.38" tar = "0.4.38"
xz2 = "0.1.7" xz2 = "0.1.7"
semver = "1.0.14" semver = "1.0.14"
dirs = "4.0.0" dirs = "4.0.0"
colored = "2.0.0" colored = "2.0.0"
zip = "0.6.2" zip = "0.6.3"
tempfile = "3.3.0" tempfile = "3.3.0"
indoc = "1.0.7" indoc = "1.0.7"
log = "0.4.17" log = "0.4.17"
env_logger = "0.9.1" env_logger = "0.9.3"
atty = "0.2.14" atty = "0.2.14"
encoding_rs_io = "0.1.7" encoding_rs_io = "0.1.7"
reqwest = { version = "0.11.12", features = ["blocking", "json", "rustls-tls", "rustls-tls-native-roots", "brotli"], default-features = false } reqwest = { version = "0.11.13", features = ["blocking", "json", "rustls-tls", "rustls-tls-native-roots", "brotli"], default-features = false }
url = "2.3.1" url = "2.3.1"
sysinfo = "0.26.7" sysinfo = "0.26.7"
thiserror = "1.0.37" thiserror = "1.0.37"
clap_complete = "3.2.5" clap_complete = "3.2.5"
anyhow = "1.0.65" anyhow = "1.0.66"
[dev-dependencies] [dev-dependencies]
pretty_assertions = "1.3.0" pretty_assertions = "1.3.0"
duct = "0.13.5" duct = "0.13.5"
shell-escape = "0.1.5"
insta = "1.21.0"
serial_test = "0.9.0"
test-log = "0.2.11" test-log = "0.2.11"
[build-dependencies] [build-dependencies]
embed-resource = "1.7.3" embed-resource = "1.7.4"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
csv = "1.1.6" csv = "1.1.6"

4
README.md

@ -24,7 +24,9 @@
### Using a script (macOS/Linux) ### Using a script (macOS/Linux)
For `bash`, `zsh` and `fish` shells, there's an [automatic installation script](./.ci/install.sh): For `bash`, `zsh` and `fish` shells, there's an [automatic installation script](./.ci/install.sh).
First ensure that `curl` and `unzip` are already installed on you operating system. Then execute:
```sh ```sh
curl -fsSL https://fnm.vercel.app/install | bash curl -fsSL https://fnm.vercel.app/install | bash

5
docs/commands.md

@ -1,7 +1,7 @@
# `fnm` # `fnm`
``` ```
fnm 1.31.1 fnm 1.32.0
A fast and simple Node.js manager A fast and simple Node.js manager
USAGE: USAGE:
@ -325,6 +325,9 @@ OPTIONS:
-h, --help -h, --help
Print help information Print help information
--json
Print JSON instead of shell commands
--log-level <LOG_LEVEL> --log-level <LOG_LEVEL>
The log level of fnm commands The log level of fnm commands

2
docs/fnm.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 30 KiB

18
e2e/__snapshots__/env.test.ts.snap

@ -0,0 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Bash outputs json: Bash 1`] = `
"set -e
fnm env --json > file.json"
`;
exports[`Fish outputs json: Fish 1`] = `"fnm env --json > file.json"`;
exports[`PowerShell outputs json: PowerShell 1`] = `
"$ErrorActionPreference = "Stop"
fnm env --json | Out-File file.json -Encoding UTF8"
`;
exports[`Zsh outputs json: Zsh 1`] = `
"set -e
fnm env --json > file.json"
`;

12
e2e/aliases.test.ts

@ -1,11 +1,11 @@
import { script } from "./shellcode/script" import { script } from "./shellcode/script.js"
import { Bash, Fish, PowerShell, Zsh } from "./shellcode/shells" import { Bash, Fish, PowerShell, Zsh } from "./shellcode/shells.js"
import describe from "./describe" import describe from "./describe.js"
import { writeFile } from "node:fs/promises" import { writeFile } from "node:fs/promises"
import path from "node:path" import path from "node:path"
import testCwd from "./shellcode/test-cwd" import testCwd from "./shellcode/test-cwd.js"
import getStderr from "./shellcode/get-stderr" import getStderr from "./shellcode/get-stderr.js"
import testNodeVersion from "./shellcode/test-node-version" import testNodeVersion from "./shellcode/test-node-version.js"
for (const shell of [Bash, Zsh, Fish, PowerShell]) { for (const shell of [Bash, Zsh, Fish, PowerShell]) {
describe(shell, () => { describe(shell, () => {

10
e2e/basic.test.ts

@ -1,10 +1,10 @@
import { writeFile, mkdir } from "node:fs/promises" import { writeFile, mkdir } from "node:fs/promises"
import { join } from "node:path" import { join } from "node:path"
import { script } from "./shellcode/script" import { script } from "./shellcode/script.js"
import { Bash, Fish, PowerShell, WinCmd, Zsh } from "./shellcode/shells" import { Bash, Fish, PowerShell, WinCmd, Zsh } from "./shellcode/shells.js"
import testCwd from "./shellcode/test-cwd" import testCwd from "./shellcode/test-cwd.js"
import testNodeVersion from "./shellcode/test-node-version" import testNodeVersion from "./shellcode/test-node-version.js"
import describe from "./describe" import describe from "./describe.js"
for (const shell of [Bash, Zsh, Fish, PowerShell, WinCmd]) { for (const shell of [Bash, Zsh, Fish, PowerShell, WinCmd]) {
describe(shell, () => { describe(shell, () => {

6
e2e/current.test.ts

@ -1,6 +1,6 @@
import { script } from "./shellcode/script" import { script } from "./shellcode/script.js"
import { Bash, Fish, PowerShell, WinCmd, Zsh } from "./shellcode/shells" import { Bash, Fish, PowerShell, WinCmd, Zsh } from "./shellcode/shells.js"
import describe from "./describe" import describe from "./describe.js"
for (const shell of [Bash, Zsh, Fish, PowerShell, WinCmd]) { for (const shell of [Bash, Zsh, Fish, PowerShell, WinCmd]) {
describe(shell, () => { describe(shell, () => {

4
e2e/describe.ts

@ -1,5 +1,5 @@
import { WinCmd } from "./shellcode/shells" import { WinCmd } from "./shellcode/shells.js"
import { Shell } from "./shellcode/shells/types" import { Shell } from "./shellcode/shells/types.js"
export default function describe( export default function describe(
shell: Pick<Shell, "name">, shell: Pick<Shell, "name">,

34
e2e/env.test.ts

@ -0,0 +1,34 @@
import { readFile } from "node:fs/promises"
import { join } from "node:path"
import { script } from "./shellcode/script.js"
import { Bash, Fish, PowerShell, WinCmd, Zsh } from "./shellcode/shells.js"
import testCwd from "./shellcode/test-cwd.js"
import describe from "./describe.js"
for (const shell of [Bash, Zsh, Fish, PowerShell, WinCmd]) {
describe(shell, () => {
test(`outputs json`, async () => {
const filename = `file.json`
await script(shell)
.then(
shell.redirectOutput(shell.call("fnm", ["env", "--json"]), {
output: filename,
})
)
.takeSnapshot(shell)
.execute(shell)
if (shell.currentlySupported()) {
const file = await readFile(join(testCwd(), filename), "utf8")
expect(JSON.parse(file)).toEqual({
FNM_ARCH: expect.any(String),
FNM_DIR: expect.any(String),
FNM_LOGLEVEL: "info",
FNM_MULTISHELL_PATH: expect.any(String),
FNM_NODE_DIST_MIRROR: expect.any(String),
FNM_VERSION_FILE_STRATEGY: "local",
})
}
})
})
}

8
e2e/exec.test.ts

@ -1,9 +1,9 @@
import { script } from "./shellcode/script" import { script } from "./shellcode/script.js"
import { Bash, Fish, PowerShell, WinCmd, Zsh } from "./shellcode/shells" import { Bash, Fish, PowerShell, WinCmd, Zsh } from "./shellcode/shells.js"
import testCwd from "./shellcode/test-cwd" import testCwd from "./shellcode/test-cwd.js"
import fs from "node:fs/promises" import fs from "node:fs/promises"
import path from "node:path" import path from "node:path"
import describe from "./describe" import describe from "./describe.js"
for (const shell of [Bash, Zsh, Fish, PowerShell, WinCmd]) { for (const shell of [Bash, Zsh, Fish, PowerShell, WinCmd]) {
describe(shell, () => { describe(shell, () => {

8
e2e/existing-installation.test.ts

@ -1,7 +1,7 @@
import getStderr from "./shellcode/get-stderr" import getStderr from "./shellcode/get-stderr.js"
import { script } from "./shellcode/script" import { script } from "./shellcode/script.js"
import { Bash, Fish, PowerShell, Zsh } from "./shellcode/shells" import { Bash, Fish, PowerShell, Zsh } from "./shellcode/shells.js"
import describe from "./describe" import describe from "./describe.js"
for (const shell of [Bash, Zsh, Fish, PowerShell]) { for (const shell of [Bash, Zsh, Fish, PowerShell]) {
describe(shell, () => { describe(shell, () => {

6
e2e/latest-lts.test.ts

@ -1,6 +1,6 @@
import { script } from "./shellcode/script" import { script } from "./shellcode/script.js"
import { Bash, Fish, PowerShell, Zsh } from "./shellcode/shells" import { Bash, Fish, PowerShell, Zsh } from "./shellcode/shells.js"
import describe from "./describe" import describe from "./describe.js"
for (const shell of [Bash, Zsh, Fish, PowerShell]) { for (const shell of [Bash, Zsh, Fish, PowerShell]) {
describe(shell, () => { describe(shell, () => {

8
e2e/log-level.test.ts

@ -1,7 +1,7 @@
import { script } from "./shellcode/script" import { script } from "./shellcode/script.js"
import { Bash, Fish, PowerShell, Zsh } from "./shellcode/shells" import { Bash, Fish, PowerShell, Zsh } from "./shellcode/shells.js"
import describe from "./describe" import describe from "./describe.js"
import getStderr from "./shellcode/get-stderr" import getStderr from "./shellcode/get-stderr.js"
for (const shell of [Bash, Zsh, Fish, PowerShell]) { for (const shell of [Bash, Zsh, Fish, PowerShell]) {
describe(shell, () => { describe(shell, () => {

8
e2e/multishell.test.ts

@ -1,7 +1,7 @@
import { script } from "./shellcode/script" import { script } from "./shellcode/script.js"
import { Bash, Fish, PowerShell, Zsh } from "./shellcode/shells" import { Bash, Fish, PowerShell, Zsh } from "./shellcode/shells.js"
import testNodeVersion from "./shellcode/test-node-version" import testNodeVersion from "./shellcode/test-node-version.js"
import describe from "./describe" import describe from "./describe.js"
for (const shell of [Bash, Zsh, Fish, PowerShell]) { for (const shell of [Bash, Zsh, Fish, PowerShell]) {
describe(shell, () => { describe(shell, () => {

8
e2e/nvmrc-lts.test.ts

@ -1,9 +1,9 @@
import { script } from "./shellcode/script" import { script } from "./shellcode/script.js"
import { Bash, Fish, PowerShell, Zsh } from "./shellcode/shells" import { Bash, Fish, PowerShell, Zsh } from "./shellcode/shells.js"
import fs from "node:fs/promises" import fs from "node:fs/promises"
import path from "node:path" import path from "node:path"
import describe from "./describe" import describe from "./describe.js"
import testCwd from "./shellcode/test-cwd" import testCwd from "./shellcode/test-cwd.js"
for (const shell of [Bash, Fish, PowerShell, Zsh]) { for (const shell of [Bash, Fish, PowerShell, Zsh]) {
describe(shell, () => { describe(shell, () => {

53
e2e/shellcode/script.ts

@ -1,13 +1,14 @@
import { ScriptLine, Shell } from "./shells/types" import { ScriptLine, Shell } from "./shells/types.js"
import execa from "execa" import { execa, type ExecaChildProcess } from "execa"
import testTmpDir from "./test-tmp-dir" import testTmpDir from "./test-tmp-dir.js"
import { Writable } from "node:stream" import { Writable } from "node:stream"
import dedent from "ts-dedent" import { dedent } from "ts-dedent"
import testCwd from "./test-cwd" import testCwd from "./test-cwd.js"
import path, { join } from "node:path" import path, { join } from "node:path"
import { writeFile } from "node:fs/promises" import { writeFile } from "node:fs/promises"
import chalk from "chalk" import chalk from "chalk"
import testBinDir from "./test-bin-dir" import testBinDir from "./test-bin-dir.js"
import { rmSync } from "node:fs"
class Script { class Script {
constructor( constructor(
@ -40,7 +41,10 @@ class Script {
const args = [...shell.launchArgs()] const args = [...shell.launchArgs()]
if (shell.forceFile) { if (shell.forceFile) {
const filename = join(testTmpDir(), "script") let filename = join(testTmpDir(), "script")
if (typeof shell.forceFile === "string") {
filename = filename + shell.forceFile
}
await writeFile(filename, [...this.lines, "exit 0"].join("\n")) await writeFile(filename, [...this.lines, "exit 0"].join("\n"))
args.push(filename) args.push(filename)
} }
@ -48,13 +52,18 @@ class Script {
const child = execa(shell.binaryName(), args, { const child = execa(shell.binaryName(), args, {
stdio: [shell.forceFile ? "ignore" : "pipe", "pipe", "pipe"], stdio: [shell.forceFile ? "ignore" : "pipe", "pipe", "pipe"],
cwd: testCwd(), cwd: testCwd(),
env: { env: (() => {
...removeAllFnmEnvVars(process.env), const newProcessEnv: Record<string, string> = {
PATH: [testBinDir(), fnmTargetDir(), process.env.PATH] ...removeAllFnmEnvVars(process.env),
.filter(Boolean) PATH: [testBinDir(), fnmTargetDir(), process.env.PATH]
.join(path.delimiter), .filter(Boolean)
FNM_DIR: this.config.fnmDir, .join(path.delimiter),
}, FNM_DIR: this.config.fnmDir,
}
delete newProcessEnv.NODE_OPTIONS
return newProcessEnv
})(),
extendEnv: false, extendEnv: false,
reject: false, reject: false,
}) })
@ -99,14 +108,14 @@ class Script {
} }
} }
function streamOutputsAndBuffer(child: execa.ExecaChildProcess) { function streamOutputsAndBuffer(child: ExecaChildProcess) {
const stdout: string[] = [] const stdout: string[] = []
const stderr: string[] = [] const stderr: string[] = []
const testName = expect.getState().currentTestName ?? "unknown" const testName = expect.getState().currentTestName ?? "unknown"
const testPath = expect.getState().testPath ?? "unknown" const testPath = expect.getState().testPath ?? "unknown"
const stdoutPrefix = chalk.yellow.dim(`[stdout] ${testPath}/${testName}: `) const stdoutPrefix = chalk.cyan.dim(`[stdout] ${testPath}/${testName}: `)
const stderrPrefix = chalk.red.dim(`[stderr] ${testPath}/${testName}: `) const stderrPrefix = chalk.magenta.dim(`[stderr] ${testPath}/${testName}: `)
if (child.stdout) { if (child.stdout) {
child.stdout.on("data", (data) => { child.stdout.on("data", (data) => {
@ -148,7 +157,8 @@ function write(writable: Writable, text: string): Promise<void> {
} }
export function script(shell: Pick<Shell, "dieOnErrors">): Script { export function script(shell: Pick<Shell, "dieOnErrors">): Script {
const fnmDir = `${testTmpDir()}/fnm` const fnmDir = path.join(testTmpDir(), "fnm")
rmSync(join(fnmDir, "aliases"), { recursive: true, force: true })
return new Script({ fnmDir }, shell.dieOnErrors ? [shell.dieOnErrors()] : []) return new Script({ fnmDir }, shell.dieOnErrors ? [shell.dieOnErrors()] : [])
} }
@ -163,8 +173,9 @@ function removeAllFnmEnvVars(obj: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
} }
function fnmTargetDir(): string { function fnmTargetDir(): string {
return path.resolve( return path.join(
__dirname, process.cwd(),
`../../target/${process.env.FNM_TARGET_NAME ?? "debug"}` "target",
process.env.FNM_TARGET_NAME ?? "debug"
) )
} }

27
e2e/shellcode/shells/index.ts → e2e/shellcode/shells.ts

@ -1,10 +1,10 @@
import { cmdCall } from "./cmdCall" import { cmdCall } from "./shells/cmdCall.js"
import { cmdEnv } from "./cmdEnv" import { cmdEnv } from "./shells/cmdEnv.js"
import { cmdExpectCommandOutput } from "./expect-command-output" import { cmdExpectCommandOutput } from "./shells/expect-command-output.js"
import { cmdHasOutputContains } from "./output-contains" import { cmdHasOutputContains } from "./shells/output-contains.js"
import { redirectOutput } from "./redirect-output" import { redirectOutput } from "./shells/redirect-output.js"
import { cmdInSubShell } from "./sub-shell" import { cmdInSubShell } from "./shells/sub-shell.js"
import { define, Shell } from "./types" import { define, Shell } from "./shells/types.js"
export const Bash = { export const Bash = {
...define<Shell>({ ...define<Shell>({
@ -34,6 +34,7 @@ export const Zsh = {
}), }),
...cmdEnv.bash, ...cmdEnv.bash,
...cmdCall.all, ...cmdCall.all,
...redirectOutput.bash,
...cmdExpectCommandOutput.bash, ...cmdExpectCommandOutput.bash,
...cmdHasOutputContains.bash, ...cmdHasOutputContains.bash,
...cmdInSubShell.zsh, ...cmdInSubShell.zsh,
@ -58,14 +59,8 @@ export const Fish = {
export const PowerShell = { export const PowerShell = {
...define<Shell>({ ...define<Shell>({
binaryName: () => { binaryName: () => "pwsh",
if (process.platform === "win32") { forceFile: ".ps1",
return "powershell.exe"
} else {
return "pwsh"
}
},
forceFile: true,
currentlySupported: () => true, currentlySupported: () => true,
name: () => "PowerShell", name: () => "PowerShell",
launchArgs: () => ["-NoProfile"], launchArgs: () => ["-NoProfile"],
@ -74,6 +69,7 @@ export const PowerShell = {
}), }),
...cmdEnv.powershell, ...cmdEnv.powershell,
...cmdCall.all, ...cmdCall.all,
...redirectOutput.powershell,
...cmdExpectCommandOutput.powershell, ...cmdExpectCommandOutput.powershell,
...cmdHasOutputContains.powershell, ...cmdHasOutputContains.powershell,
...cmdInSubShell.powershell, ...cmdInSubShell.powershell,
@ -97,4 +93,5 @@ export const WinCmd = {
...cmdEnv.wincmd, ...cmdEnv.wincmd,
...cmdCall.all, ...cmdCall.all,
...cmdExpectCommandOutput.wincmd, ...cmdExpectCommandOutput.wincmd,
...redirectOutput.bash,
} }

2
e2e/shellcode/shells/cmdCall.ts

@ -1,4 +1,4 @@
import { define, ScriptLine } from "./types" import { define, ScriptLine } from "./types.js"
export type HasCall = { export type HasCall = {
call: (binary: string, args: string[]) => ScriptLine call: (binary: string, args: string[]) => ScriptLine

2
e2e/shellcode/shells/cmdEnv.ts

@ -1,4 +1,4 @@
import { ScriptLine, define } from "./types" import { ScriptLine, define } from "./types.js"
type EnvConfig = { useOnCd: boolean; logLevel: string } type EnvConfig = { useOnCd: boolean; logLevel: string }
export type HasEnv = { env(cfg: Partial<EnvConfig>): ScriptLine } export type HasEnv = { env(cfg: Partial<EnvConfig>): ScriptLine }

4
e2e/shellcode/shells/expect-command-output.ts

@ -1,5 +1,5 @@
import dedent from "ts-dedent" import { dedent } from "ts-dedent"
import { define, ScriptLine } from "./types" import { define, ScriptLine } from "./types.js"
export type HasExpectCommandOutput = { export type HasExpectCommandOutput = {
hasCommandOutput( hasCommandOutput(

2
e2e/shellcode/shells/output-contains.ts

@ -1,4 +1,4 @@
import { define, ScriptLine } from "./types" import { define, ScriptLine } from "./types.js"
export type HasOutputContains = { export type HasOutputContains = {
scriptOutputContains(script: ScriptLine, substring: string): ScriptLine scriptOutputContains(script: ScriptLine, substring: string): ScriptLine

7
e2e/shellcode/shells/redirect-output.ts

@ -1,4 +1,4 @@
import { ScriptLine, define } from "./types" import { ScriptLine, define } from "./types.js"
type RedirectOutputOpts = { output: string } type RedirectOutputOpts = { output: string }
export type HasRedirectOutput = { export type HasRedirectOutput = {
@ -7,7 +7,10 @@ export type HasRedirectOutput = {
export const redirectOutput = { export const redirectOutput = {
bash: define<HasRedirectOutput>({ bash: define<HasRedirectOutput>({
redirectOutput: (childCommand, opts) => `${childCommand} > ${opts.output}`,
}),
powershell: define<HasRedirectOutput>({
redirectOutput: (childCommand, opts) => redirectOutput: (childCommand, opts) =>
`(${childCommand}) > ${opts.output}`, `${childCommand} | Out-File ${opts.output} -Encoding UTF8`,
}), }),
} }

2
e2e/shellcode/shells/sub-shell.ts

@ -1,4 +1,4 @@
import { ScriptLine, define } from "./types" import { ScriptLine, define } from "./types.js"
import quote from "shell-escape" import quote from "shell-escape"
type HasInSubShell = { inSubShell: (script: ScriptLine) => ScriptLine } type HasInSubShell = { inSubShell: (script: ScriptLine) => ScriptLine }

2
e2e/shellcode/shells/types.ts

@ -5,7 +5,7 @@ export type Shell = {
name(): string name(): string
launchArgs(): string[] launchArgs(): string[]
dieOnErrors?(): string dieOnErrors?(): string
forceFile?: true forceFile?: true | string
} }
export type ScriptLine = string export type ScriptLine = string

2
e2e/shellcode/test-bin-dir.ts

@ -1,6 +1,6 @@
import { mkdirSync } from "node:fs" import { mkdirSync } from "node:fs"
import path from "node:path" import path from "node:path"
import testTmpDir from "./test-tmp-dir" import testTmpDir from "./test-tmp-dir.js"
export default function testBinDir() { export default function testBinDir() {
const dir = path.join(testTmpDir(), "bin") const dir = path.join(testTmpDir(), "bin")

2
e2e/shellcode/test-cwd.ts

@ -1,6 +1,6 @@
import { mkdirSync } from "node:fs" import { mkdirSync } from "node:fs"
import path from "node:path" import path from "node:path"
import testTmpDir from "./test-tmp-dir" import testTmpDir from "./test-tmp-dir.js"
export default function testCwd() { export default function testCwd() {
const dir = path.join(testTmpDir(), "cwd") const dir = path.join(testTmpDir(), "cwd")

6
e2e/shellcode/test-node-version.ts

@ -1,6 +1,6 @@
import { HasCall } from "./shells/cmdCall" import { HasCall } from "./shells/cmdCall.js"
import { ScriptLine } from "./shells/types" import { ScriptLine } from "./shells/types.js"
import { HasExpectCommandOutput } from "./shells/expect-command-output" import { HasExpectCommandOutput } from "./shells/expect-command-output.js"
export default function testNodeVersion< export default function testNodeVersion<
S extends HasCall & HasExpectCommandOutput S extends HasCall & HasExpectCommandOutput

3
e2e/shellcode/test-tmp-dir.ts

@ -1,4 +1,4 @@
import { mkdirSync, rmSync } from "node:fs" import { mkdirSync } from "node:fs"
import { tmpdir } from "node:os" import { tmpdir } from "node:os"
import { join } from "node:path" import { join } from "node:path"
@ -9,7 +9,6 @@ export default function testTmpDir(): string {
.replace(/_+/g, "_") .replace(/_+/g, "_")
const tmpDir = join(tmpdir(), `shellcode/${testName}`) const tmpDir = join(tmpdir(), `shellcode/${testName}`)
mkdirSync(tmpDir, { recursive: true }) mkdirSync(tmpDir, { recursive: true })
rmSync(join(tmpDir, "fnm/aliases"), { recursive: true, force: true })
return tmpDir return tmpDir
} }

52
e2e/system-node.test.ts

@ -1,10 +1,10 @@
import { script } from "./shellcode/script" import { script } from "./shellcode/script.js"
import { Bash, Fish, PowerShell, WinCmd, Zsh } from "./shellcode/shells" import { Bash, Fish, PowerShell, WinCmd, Zsh } from "./shellcode/shells.js"
import fs from "node:fs/promises" import fs from "node:fs/promises"
import path from "node:path" import path from "node:path"
import describe from "./describe" import describe from "./describe.js"
import testNodeVersion from "./shellcode/test-node-version" import testNodeVersion from "./shellcode/test-node-version.js"
import testBinDir from "./shellcode/test-bin-dir" import testBinDir from "./shellcode/test-bin-dir.js"
for (const shell of [Bash, Fish, PowerShell, WinCmd, Zsh]) { for (const shell of [Bash, Fish, PowerShell, WinCmd, Zsh]) {
describe(shell, () => { describe(shell, () => {
@ -12,18 +12,7 @@ for (const shell of [Bash, Fish, PowerShell, WinCmd, Zsh]) {
const t = process.platform === "darwin" && shell === Bash ? test.skip : test const t = process.platform === "darwin" && shell === Bash ? test.skip : test
t(`switches to system node`, async () => { t(`switches to system node`, async () => {
const customNode = path.join(testBinDir(), "node") await writeCustomNode()
if (
process.platform === "win32" &&
[WinCmd, PowerShell].includes(shell)
) {
await fs.writeFile(customNode + ".cmd", '@echo "custom node"')
} else {
await fs.writeFile(customNode, `#!/bin/bash\n\necho "custom"\n`)
// set executable
await fs.chmod(customNode, 0o766)
}
await script(shell) await script(shell)
.then(shell.env({})) .then(shell.env({}))
@ -34,5 +23,34 @@ for (const shell of [Bash, Fish, PowerShell, WinCmd, Zsh]) {
.then(testNodeVersion(shell, "custom")) .then(testNodeVersion(shell, "custom"))
.execute(shell) .execute(shell)
}) })
t(`aliasing a system node`, async () => {
writeCustomNode()
const init = script(shell).then(shell.env({}))
await init
.then(shell.call("fnm", ["install", "v10.10.0"]))
.then(shell.call("fnm", ["use", "v10"]))
.then(shell.call("fnm", ["default", "10"]))
.execute(shell)
await init
.then(testNodeVersion(shell, "v10.10.0"))
.then(shell.call("fnm", ["default", "system"]))
.execute(shell)
await init.then(testNodeVersion(shell, "custom")).execute(shell)
})
}) })
async function writeCustomNode() {
const customNode = path.join(testBinDir(), "node")
if (process.platform === "win32" && [WinCmd, PowerShell].includes(shell)) {
await fs.writeFile(customNode + ".cmd", "@echo custom")
} else {
await fs.writeFile(customNode, `#!/bin/bash\n\necho "custom"\n`)
// set executable
await fs.chmod(customNode, 0o766)
}
}
} }

6
e2e/uninstall.test.ts

@ -1,6 +1,6 @@
import { script } from "./shellcode/script" import { script } from "./shellcode/script.js"
import { Bash, Fish, PowerShell, Zsh } from "./shellcode/shells" import { Bash, Fish, PowerShell, Zsh } from "./shellcode/shells.js"
import describe from "./describe" import describe from "./describe.js"
for (const shell of [Bash, Zsh, Fish, PowerShell]) { for (const shell of [Bash, Zsh, Fish, PowerShell]) {
describe(shell, () => { describe(shell, () => {

22
jest.config.cjs

@ -0,0 +1,22 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: "ts-jest/presets/default-esm",
testEnvironment: "node",
testTimeout: 120000,
extensionsToTreatAsEsm: [".ts"],
moduleNameMapper: {
"^(\\.{1,2}/.*)\\.js$": "$1",
"#ansi-styles": "ansi-styles/index.js",
"#supports-color": "supports-color/index.js",
},
transform: {
// '^.+\\.[tj]sx?$' to process js/ts with `ts-jest`
// '^.+\\.m?[tj]sx?$' to process js/ts/mjs/mts with `ts-jest`
"^.+\\.tsx?$": [
"ts-jest",
{
useESM: true,
},
],
},
}

29
package.json

@ -1,13 +1,14 @@
{ {
"name": "fnm", "name": "fnm",
"version": "1.31.1", "version": "1.32.0",
"private": true, "private": true,
"repository": "git@github.com:Schniz/fnm.git", "repository": "git@github.com:Schniz/fnm.git",
"author": "Gal Schlezinger <gal@spitfire.co.il>", "author": "Gal Schlezinger <gal@spitfire.co.il>",
"packageManager": "pnpm@7.13.2", "type": "module",
"packageManager": "pnpm@7.16.1",
"license": "GPLv3", "license": "GPLv3",
"scripts": { "scripts": {
"test": "jest", "test": "cross-env NODE_OPTIONS='--experimental-vm-modules' jest",
"version:prepare": "changeset version && ./.ci/prepare-version.js", "version:prepare": "changeset version && ./.ci/prepare-version.js",
"generate-command-docs": "./.ci/print-command-docs.js" "generate-command-docs": "./.ci/print-command-docs.js"
}, },
@ -21,39 +22,27 @@
} }
}, },
"devDependencies": { "devDependencies": {
"@changesets/cli": "2.25.0", "@changesets/cli": "2.25.2",
"@svitejs/changesets-changelog-github-compact": "0.1.1", "@svitejs/changesets-changelog-github-compact": "0.1.1",
"@swc-node/jest": "^1.5.5",
"@swc/core": "^1.3.17",
"@types/jest": "^29.2.3", "@types/jest": "^29.2.3",
"@types/node": "^18.11.9", "@types/node": "^18.11.9",
"@types/shell-escape": "^0.2.1", "@types/shell-escape": "^0.2.1",
"chalk": "4", "chalk": "^5.1.2",
"cmd-ts": "0.11.0", "cmd-ts": "0.11.0",
"execa": "5.1.1", "cross-env": "^7.0.3",
"execa": "6.1.0",
"jest": "^29.3.1", "jest": "^29.3.1",
"lerna-changelog": "2.2.0", "lerna-changelog": "2.2.0",
"p-retry": "^4",
"prettier": "2.7.1", "prettier": "2.7.1",
"pv": "1.0.1", "pv": "1.0.1",
"shell-escape": "^0.2.0", "shell-escape": "^0.2.0",
"svg-term-cli": "2.1.1", "svg-term-cli": "2.1.1",
"toml": "3.0.0", "toml": "3.0.0",
"ts-dedent": "^2.2.0", "ts-dedent": "^2.2.0",
"ts-jest": "^29.0.3",
"typescript": "^4.8.4" "typescript": "^4.8.4"
}, },
"prettier": { "prettier": {
"semi": false "semi": false
},
"jest": {
"transform": {
"^.+\\.ts$": "@swc-node/jest"
},
"testEnvironment": "node",
"moduleFileExtensions": [
"ts",
"js"
],
"testTimeout": 120000
} }
} }

477
pnpm-lock.yaml

@ -1,47 +1,45 @@
lockfileVersion: 5.4 lockfileVersion: 5.4
specifiers: specifiers:
'@changesets/cli': 2.25.0 '@changesets/cli': 2.25.2
'@svitejs/changesets-changelog-github-compact': 0.1.1 '@svitejs/changesets-changelog-github-compact': 0.1.1
'@swc-node/jest': ^1.5.5
'@swc/core': ^1.3.17
'@types/jest': ^29.2.3 '@types/jest': ^29.2.3
'@types/node': ^18.11.9 '@types/node': ^18.11.9
'@types/shell-escape': ^0.2.1 '@types/shell-escape': ^0.2.1
chalk: '4' chalk: ^5.1.2
cmd-ts: 0.11.0 cmd-ts: 0.11.0
execa: 5.1.1 cross-env: ^7.0.3
execa: 6.1.0
jest: ^29.3.1 jest: ^29.3.1
lerna-changelog: 2.2.0 lerna-changelog: 2.2.0
p-retry: ^4
prettier: 2.7.1 prettier: 2.7.1
pv: 1.0.1 pv: 1.0.1
shell-escape: ^0.2.0 shell-escape: ^0.2.0
svg-term-cli: 2.1.1 svg-term-cli: 2.1.1
toml: 3.0.0 toml: 3.0.0
ts-dedent: ^2.2.0 ts-dedent: ^2.2.0
ts-jest: ^29.0.3
typescript: ^4.8.4 typescript: ^4.8.4
devDependencies: devDependencies:
'@changesets/cli': 2.25.0 '@changesets/cli': 2.25.2
'@svitejs/changesets-changelog-github-compact': 0.1.1 '@svitejs/changesets-changelog-github-compact': 0.1.1
'@swc-node/jest': 1.5.5_ndsvmk6usi2zctqgdwwiwj3k5q
'@swc/core': 1.3.17
'@types/jest': 29.2.3 '@types/jest': 29.2.3
'@types/node': 18.11.9 '@types/node': 18.11.9
'@types/shell-escape': 0.2.1 '@types/shell-escape': 0.2.1
chalk: 4.1.2 chalk: 5.1.2
cmd-ts: 0.11.0 cmd-ts: 0.11.0
execa: 5.1.1 cross-env: 7.0.3
execa: 6.1.0
jest: 29.3.1_@types+node@18.11.9 jest: 29.3.1_@types+node@18.11.9
lerna-changelog: 2.2.0 lerna-changelog: 2.2.0
p-retry: 4.6.2
prettier: 2.7.1 prettier: 2.7.1
pv: 1.0.1 pv: 1.0.1
shell-escape: 0.2.0 shell-escape: 0.2.0
svg-term-cli: 2.1.1 svg-term-cli: 2.1.1
toml: 3.0.0 toml: 3.0.0
ts-dedent: 2.2.0 ts-dedent: 2.2.0
ts-jest: 29.0.3_r24ewcothphvclnu77pxb4u4se
typescript: 4.8.4 typescript: 4.8.4
packages: packages:
@ -397,8 +395,8 @@ packages:
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
dev: true dev: true
/@changesets/apply-release-plan/6.1.1: /@changesets/apply-release-plan/6.1.2:
resolution: {integrity: sha512-LaQiP/Wf0zMVR0HNrLQAjz3rsNsr0d/RlnP6Ef4oi8VafOwnY1EoWdK4kssuUJGgNgDyHpomS50dm8CU3D7k7g==} resolution: {integrity: sha512-H8TV9E/WtJsDfoDVbrDGPXmkZFSv7W2KLqp4xX4MKZXshb0hsQZUNowUa8pnus9qb/5OZrFFRVsUsDCVHNW/AQ==}
dependencies: dependencies:
'@babel/runtime': 7.18.6 '@babel/runtime': 7.18.6
'@changesets/config': 2.2.0 '@changesets/config': 2.2.0
@ -432,12 +430,12 @@ packages:
'@changesets/types': 5.2.0 '@changesets/types': 5.2.0
dev: true dev: true
/@changesets/cli/2.25.0: /@changesets/cli/2.25.2:
resolution: {integrity: sha512-Svu5KD2enurVHGEEzCRlaojrHjVYgF9srmMP9VQSy9c1TspX6C9lDPpulsSNIjYY9BuU/oiWpjBgR7RI9eQiAA==} resolution: {integrity: sha512-ACScBJXI3kRyMd2R8n8SzfttDHi4tmKSwVwXBazJOylQItSRSF4cGmej2E4FVf/eNfGy6THkL9GzAahU9ErZrA==}
hasBin: true hasBin: true
dependencies: dependencies:
'@babel/runtime': 7.18.6 '@babel/runtime': 7.18.6
'@changesets/apply-release-plan': 6.1.1 '@changesets/apply-release-plan': 6.1.2
'@changesets/assemble-release-plan': 5.2.2 '@changesets/assemble-release-plan': 5.2.2
'@changesets/changelog-git': 0.1.13 '@changesets/changelog-git': 0.1.13
'@changesets/config': 2.2.0 '@changesets/config': 2.2.0
@ -449,7 +447,7 @@ packages:
'@changesets/pre': 1.0.13 '@changesets/pre': 1.0.13
'@changesets/read': 0.5.8 '@changesets/read': 0.5.8
'@changesets/types': 5.2.0 '@changesets/types': 5.2.0
'@changesets/write': 0.2.1 '@changesets/write': 0.2.2
'@manypkg/get-packages': 1.1.3 '@manypkg/get-packages': 1.1.3
'@types/is-ci': 3.0.0 '@types/is-ci': 3.0.0
'@types/semver': 6.2.3 '@types/semver': 6.2.3
@ -579,8 +577,8 @@ packages:
resolution: {integrity: sha512-km/66KOqJC+eicZXsm2oq8A8bVTSpkZJ60iPV/Nl5Z5c7p9kk8xxh6XGRTlnludHldxOOfudhnDN2qPxtHmXzA==} resolution: {integrity: sha512-km/66KOqJC+eicZXsm2oq8A8bVTSpkZJ60iPV/Nl5Z5c7p9kk8xxh6XGRTlnludHldxOOfudhnDN2qPxtHmXzA==}
dev: true dev: true
/@changesets/write/0.2.1: /@changesets/write/0.2.2:
resolution: {integrity: sha512-KUd49nt2fnYdGixIqTi1yVE1nAoZYUMdtB3jBfp77IMqjZ65hrmZE5HdccDlTeClZN0420ffpnfET3zzeY8pdw==} resolution: {integrity: sha512-kCYNHyF3xaId1Q/QE+DF3UTrHTyg3Cj/f++T8S8/EkC+jh1uK2LFnM9h+EzV+fsmnZDrs7r0J4LLpeI/VWC5Hg==}
dependencies: dependencies:
'@babel/runtime': 7.18.6 '@babel/runtime': 7.18.6
'@changesets/types': 5.2.0 '@changesets/types': 5.2.0
@ -907,142 +905,6 @@ packages:
rimraf: 2.7.1 rimraf: 2.7.1
dev: true dev: true
/@node-rs/xxhash-android-arm-eabi/1.2.1:
resolution: {integrity: sha512-wvx3/7zBlsUnwWS9ZHBuPqubKaotMNDcyyroEMt5ZV+28/hF4HjNZQgOk6uHdOdlMKAXThqc6AHzwGOK1XXYmw==}
engines: {node: '>= 12'}
cpu: [arm]
os: [android]
requiresBuild: true
dev: true
optional: true
/@node-rs/xxhash-android-arm64/1.2.1:
resolution: {integrity: sha512-97Hrljvg+J4VxZH5WBqTwIsWrkNCub4aj2mW878svOVYV1qQFU0D4LuJGX8qE0WFTrmL0ycMEH2WIk4CIV5dMg==}
engines: {node: '>= 12'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@node-rs/xxhash-darwin-arm64/1.2.1:
resolution: {integrity: sha512-TtfW7Zo1AYQcIMYBI46VQtNkS6scXRnPp/f+9b7+xzEZ72SIpaDKE/33EjrXBNm2ARYRxCpTtL4jN0IHECYe3g==}
engines: {node: '>= 12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@node-rs/xxhash-darwin-x64/1.2.1:
resolution: {integrity: sha512-EEy3bPzvi1TStTMxalvIyNia6j9vlICTjnwRhGpShfIMg6XR6OUzda9JojozF53YhHH4oPAzUtUGA2SM5wU8mg==}
engines: {node: '>= 12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@node-rs/xxhash-freebsd-x64/1.2.1:
resolution: {integrity: sha512-FYBrXV15HXnDEbzIJ0veY5H0C3NqU8kVJOvX7pAFK2kGqspq92BGfBS8j1BOxEFZ6pyQwMQgfOhRI/bsdrb7Zg==}
engines: {node: '>= 12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@node-rs/xxhash-linux-arm-gnueabihf/1.2.1:
resolution: {integrity: sha512-h58lDRP3T+kNjH4GCfnCB50b7d9XuVLogN3+wRWZI0yDE1AlTZBfK/00IwSvREcRjHqc5crsS9Zh3SuxIjID2g==}
engines: {node: '>= 12'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@node-rs/xxhash-linux-arm64-gnu/1.2.1:
resolution: {integrity: sha512-OLReGi3amHkIU8zPxmDXyJHsUNR2XOPBwd9bzJcuxNM0wSP0g3EMs9VUHmlhh/DiomTduu6A90WzrmuCGaYUAA==}
engines: {node: '>= 12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@node-rs/xxhash-linux-arm64-musl/1.2.1:
resolution: {integrity: sha512-Rc22qQALjoTkdY2JnoJZn6bzHQQyNcvysU1kznAqwg9pPOD4bU++of1sS7II8o/ihe1g/qz4RKfyG+qUPjy/WQ==}
engines: {node: '>= 12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@node-rs/xxhash-linux-x64-gnu/1.2.1:
resolution: {integrity: sha512-MM0dfHxEe4uHuUFyM12pXiuie7fAq2LH7+PrXb8FqaGHqxF4aaIZE0gTMNDmrCsJy91tun85/TCu1vQEJ0g+Bw==}
engines: {node: '>= 12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@node-rs/xxhash-linux-x64-musl/1.2.1:
resolution: {integrity: sha512-mD3DEGqnxi39cMDIi5ZqcEUl4/eOcM4qGY/XFSFpUN7oUG4vnf/8U5mwzvAjU+nHWaq9ubteePA1GOXiATvv6w==}
engines: {node: '>= 12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@node-rs/xxhash-win32-arm64-msvc/1.2.1:
resolution: {integrity: sha512-90A9Ux4ozMSmss3Pn+A3TTYSw8uzGSBIPrRw+mIn/rVKBMfMfkKKNL+xjIXw1lkL9s2kG/1aGMBRTeuOeG9xtA==}
engines: {node: '>= 12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@node-rs/xxhash-win32-ia32-msvc/1.2.1:
resolution: {integrity: sha512-zhpg4pV71e6vDmOSLwFCNKiodtcNHUg76d5npvkWhJa0T/ykMHGWUbaFnpQzLwGD5Sq4mDHrQ7h6lxYA4j/6eQ==}
engines: {node: '>= 12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@node-rs/xxhash-win32-x64-msvc/1.2.1:
resolution: {integrity: sha512-KkZKyppIM2HJA3oZQ4nsPLwQ5HbELCZt+epLAH3/0H3QN9zqbzoCn6b140hg0lX+KT6YT0otGMbnlnvD2qpswQ==}
engines: {node: '>= 12'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@node-rs/xxhash/1.2.1:
resolution: {integrity: sha512-Yyur0X8LFgzxcRWro1wAGVCZK8kcpDhnRKUUeW21OTpoHzDwN8zZSm43bF76xMQF1SyfWr17aSG6G0o8D1hTJA==}
engines: {node: '>= 12'}
optionalDependencies:
'@node-rs/xxhash-android-arm-eabi': 1.2.1
'@node-rs/xxhash-android-arm64': 1.2.1
'@node-rs/xxhash-darwin-arm64': 1.2.1
'@node-rs/xxhash-darwin-x64': 1.2.1
'@node-rs/xxhash-freebsd-x64': 1.2.1
'@node-rs/xxhash-linux-arm-gnueabihf': 1.2.1
'@node-rs/xxhash-linux-arm64-gnu': 1.2.1
'@node-rs/xxhash-linux-arm64-musl': 1.2.1
'@node-rs/xxhash-linux-x64-gnu': 1.2.1
'@node-rs/xxhash-linux-x64-musl': 1.2.1
'@node-rs/xxhash-win32-arm64-msvc': 1.2.1
'@node-rs/xxhash-win32-ia32-msvc': 1.2.1
'@node-rs/xxhash-win32-x64-msvc': 1.2.1
dev: true
/@nodelib/fs.scandir/2.1.5: /@nodelib/fs.scandir/2.1.5:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
@ -1181,162 +1043,6 @@ packages:
- encoding - encoding
dev: true dev: true
/@swc-node/core/1.9.1_@swc+core@1.3.17:
resolution: {integrity: sha512-Mh4T/PmQOpPtqw1BNvU38uWzsXbd5RJji17YBXnj7JDDE5KlTR9sSo2RKxWKDVtHbdcD1S+CtyZXA93aEWlfGQ==}
engines: {node: '>= 10'}
peerDependencies:
'@swc/core': '>= 1.3'
dependencies:
'@swc/core': 1.3.17
dev: true
/@swc-node/jest/1.5.5_ndsvmk6usi2zctqgdwwiwj3k5q:
resolution: {integrity: sha512-DUf0XMk5xuwt4x+BiZ++KlPxa0H9lvjhFdXU2c8lsu+UztGRUAUtI5nhNTqjS9PXw0zzf3gEFP7M8hNUo52YhQ==}
peerDependencies:
'@swc/core': '>= 1.3'
dependencies:
'@node-rs/xxhash': 1.2.1
'@swc-node/core': 1.9.1_@swc+core@1.3.17
'@swc-node/register': 1.5.4_ndsvmk6usi2zctqgdwwiwj3k5q
'@swc/core': 1.3.17
transitivePeerDependencies:
- supports-color
- typescript
dev: true
/@swc-node/register/1.5.4_ndsvmk6usi2zctqgdwwiwj3k5q:
resolution: {integrity: sha512-cM5/A63bO6qLUFC4gcBnOlQO5yd8ObSdFUIp7sXf11Oq5mPVAnJy2DqjbWMUsqUaHuNk+lOIt76ie4DEseUIyA==}
peerDependencies:
'@swc/core': '>= 1.3'
typescript: '>= 4.3'
dependencies:
'@swc-node/core': 1.9.1_@swc+core@1.3.17
'@swc-node/sourcemap-support': 0.2.2
'@swc/core': 1.3.17
colorette: 2.0.19
debug: 4.3.4
pirates: 4.0.5
tslib: 2.4.1
typescript: 4.8.4
transitivePeerDependencies:
- supports-color
dev: true
/@swc-node/sourcemap-support/0.2.2:
resolution: {integrity: sha512-PA4p7nC5LwPdEVcQXFxMTpfvizYPeMoB55nIIx+yC3FiLnyPgC2hcpUitPy5h8RRGdCZ/Mvb2ryEcVYS8nI6YA==}
dependencies:
source-map-support: 0.5.21
tslib: 2.4.1
dev: true
/@swc/core-darwin-arm64/1.3.17:
resolution: {integrity: sha512-l82ub46GSRRpPpvR9NkHqqskPKQAkpRAKMZNzBrY01GLzUj63XH8uAv+3N1Htpq835iekWJjGZXYONCZ1eHlcQ==}
engines: {node: '>=10'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@swc/core-darwin-x64/1.3.17:
resolution: {integrity: sha512-pBVrF1Ac5t6v9wd5Jqdfve7d0G3ELnjHzaEmCxV0W3ma7/pfHiGv7ZPGnXTclt0+IrMisrVoY5LM+xG1Hv+91Q==}
engines: {node: '>=10'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@swc/core-linux-arm-gnueabihf/1.3.17:
resolution: {integrity: sha512-argIcltgNEssehO7PzXuNYbV7ubVd6YW13FhB9uNWEp/1HGSzIJEnJ4tISbZe9NA9jwI3IALAiE3vDdTdGf/XA==}
engines: {node: '>=10'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@swc/core-linux-arm64-gnu/1.3.17:
resolution: {integrity: sha512-bH0YSJd3thV1/kY5lHkeXTEcbx0HYFpIqCGJbeDvzpg38FEFSFiUw9+0VX6Yf3FKPq1DcGK/eAKYC7LeZfAzvg==}
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@swc/core-linux-arm64-musl/1.3.17:
resolution: {integrity: sha512-sV5rfpPADmOg5YfTXLLGAzeTUjjgix29kra44EKa25Qyuwu/52OkoW4olF86qwvcQrRySWwga95EeClRhIX/zA==}
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@swc/core-linux-x64-gnu/1.3.17:
resolution: {integrity: sha512-KbPdQSLgSjTutFsCGzjTzMCzZBiem4dEByiKxkrQmt87kLX4wLlPCy0Gppcaa6jExRcvy5co/vlf9TknuKcy4A==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@swc/core-linux-x64-musl/1.3.17:
resolution: {integrity: sha512-NDUbaJ0MbklioadPhkzQDOP8eazOn1lJmNmcmg9zNUR7GZsek1CMmJkyJ3ETXZ5gbIfVQ1hPkPm23P9vQBtCdQ==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@swc/core-win32-arm64-msvc/1.3.17:
resolution: {integrity: sha512-fmpGG1iuKcMSDSO2ZHETlYTRel5hTwyuJ4AYbUX3/YzAE4iEnV1PI/n54wrWIdKONsPt3Pcj/UyRD3A6/ZS8rQ==}
engines: {node: '>=10'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@swc/core-win32-ia32-msvc/1.3.17:
resolution: {integrity: sha512-hmQovtwsKnb4D+NAKN2HYjTyNE3iXakBrQNSo1u+/KeMU+D6/+XCSdzrg7ob4olqf4yq52FXPPkLYwyDDHRO/g==}
engines: {node: '>=10'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@swc/core-win32-x64-msvc/1.3.17:
resolution: {integrity: sha512-wl+L7300uFr74WWw+b+mIx9NzMQB8+LCapFc01LqDgFyUFT6HSIIM9/s3zzy5Xh8UpgnkbS0/7rMl+zC1V+nFw==}
engines: {node: '>=10'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@swc/core/1.3.17:
resolution: {integrity: sha512-wcjmxJqwpo9mQqnyUChyIIxC3nw2xRK07zGMS2zbcId31J+wer5PMbgy0qEqzZEeIswUmL8lBO4whM+wSAmVqA==}
engines: {node: '>=10'}
hasBin: true
requiresBuild: true
optionalDependencies:
'@swc/core-darwin-arm64': 1.3.17
'@swc/core-darwin-x64': 1.3.17
'@swc/core-linux-arm-gnueabihf': 1.3.17
'@swc/core-linux-arm64-gnu': 1.3.17
'@swc/core-linux-arm64-musl': 1.3.17
'@swc/core-linux-x64-gnu': 1.3.17
'@swc/core-linux-x64-musl': 1.3.17
'@swc/core-win32-arm64-msvc': 1.3.17
'@swc/core-win32-ia32-msvc': 1.3.17
'@swc/core-win32-x64-msvc': 1.3.17
dev: true
/@tootallnate/once/1.1.2: /@tootallnate/once/1.1.2:
resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==}
engines: {node: '>= 6'} engines: {node: '>= 6'}
@ -1463,10 +1169,6 @@ packages:
resolution: {integrity: sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==} resolution: {integrity: sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==}
dev: true dev: true
/@types/retry/0.12.0:
resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==}
dev: true
/@types/semver/6.2.3: /@types/semver/6.2.3:
resolution: {integrity: sha512-KQf+QAMWKMrtBMsB8/24w53tEsxllMj6TuA80TT/5igJalLI/zm0L3oXRbIAl4Ohfc85gyHX/jhMwsVkmhLU4A==} resolution: {integrity: sha512-KQf+QAMWKMrtBMsB8/24w53tEsxllMj6TuA80TT/5igJalLI/zm0L3oXRbIAl4Ohfc85gyHX/jhMwsVkmhLU4A==}
dev: true dev: true
@ -1758,6 +1460,13 @@ packages:
update-browserslist-db: 1.0.10_browserslist@4.21.4 update-browserslist-db: 1.0.10_browserslist@4.21.4
dev: true dev: true
/bs-logger/0.2.6:
resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==}
engines: {node: '>= 6'}
dependencies:
fast-json-stable-stringify: 2.1.0
dev: true
/bser/2.1.1: /bser/2.1.1:
resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
dependencies: dependencies:
@ -1864,6 +1573,11 @@ packages:
supports-color: 7.2.0 supports-color: 7.2.0
dev: true dev: true
/chalk/5.1.2:
resolution: {integrity: sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
dev: true
/char-regex/1.0.2: /char-regex/1.0.2:
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -1994,10 +1708,6 @@ packages:
color-string: 1.9.1 color-string: 1.9.1
dev: true dev: true
/colorette/2.0.19:
resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==}
dev: true
/command-exists/1.2.9: /command-exists/1.2.9:
resolution: {integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==} resolution: {integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==}
dev: true dev: true
@ -2018,6 +1728,14 @@ packages:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
dev: true dev: true
/cross-env/7.0.3:
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
hasBin: true
dependencies:
cross-spawn: 7.0.3
dev: true
/cross-spawn-async/2.2.5: /cross-spawn-async/2.2.5:
resolution: {integrity: sha512-snteb3aVrxYYOX9e8BabYFK9WhCDhTlw1YQktfTthBogxri4/2r9U2nQc0ffY73ZAxezDc+U8gvHAeU1wy1ubQ==} resolution: {integrity: sha512-snteb3aVrxYYOX9e8BabYFK9WhCDhTlw1YQktfTthBogxri4/2r9U2nQc0ffY73ZAxezDc+U8gvHAeU1wy1ubQ==}
deprecated: cross-spawn no longer requires a build toolchain, use it instead deprecated: cross-spawn no longer requires a build toolchain, use it instead
@ -2378,6 +2096,21 @@ packages:
strip-final-newline: 2.0.0 strip-final-newline: 2.0.0
dev: true dev: true
/execa/6.1.0:
resolution: {integrity: sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
cross-spawn: 7.0.3
get-stream: 6.0.1
human-signals: 3.0.1
is-stream: 3.0.0
merge-stream: 2.0.0
npm-run-path: 5.1.0
onetime: 6.0.0
signal-exit: 3.0.7
strip-final-newline: 3.0.0
dev: true
/exit/0.1.2: /exit/0.1.2:
resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
@ -2728,6 +2461,11 @@ packages:
engines: {node: '>=10.17.0'} engines: {node: '>=10.17.0'}
dev: true dev: true
/human-signals/3.0.1:
resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==}
engines: {node: '>=12.20.0'}
dev: true
/humanize-ms/1.2.1: /humanize-ms/1.2.1:
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
dependencies: dependencies:
@ -2940,6 +2678,11 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dev: true dev: true
/is-stream/3.0.0:
resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dev: true
/is-string/1.0.7: /is-string/1.0.7:
resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -3554,6 +3297,10 @@ packages:
p-locate: 5.0.0 p-locate: 5.0.0
dev: true dev: true
/lodash.memoize/4.1.2:
resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==}
dev: true
/lodash.startcase/4.4.0: /lodash.startcase/4.4.0:
resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==}
dev: true dev: true
@ -3608,6 +3355,10 @@ packages:
semver: 6.3.0 semver: 6.3.0
dev: true dev: true
/make-error/1.3.6:
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
dev: true
/make-fetch-happen/9.1.0: /make-fetch-happen/9.1.0:
resolution: {integrity: sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==} resolution: {integrity: sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@ -3712,6 +3463,11 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
dev: true dev: true
/mimic-fn/4.0.0:
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
engines: {node: '>=12'}
dev: true
/min-indent/1.0.1: /min-indent/1.0.1:
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
engines: {node: '>=4'} engines: {node: '>=4'}
@ -3895,6 +3651,13 @@ packages:
path-key: 3.1.1 path-key: 3.1.1
dev: true dev: true
/npm-run-path/5.1.0:
resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
path-key: 4.0.0
dev: true
/nth-check/1.0.2: /nth-check/1.0.2:
resolution: {integrity: sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==} resolution: {integrity: sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==}
dependencies: dependencies:
@ -3962,6 +3725,13 @@ packages:
mimic-fn: 2.1.0 mimic-fn: 2.1.0
dev: true dev: true
/onetime/6.0.0:
resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
engines: {node: '>=12'}
dependencies:
mimic-fn: 4.0.0
dev: true
/os-tmpdir/1.0.2: /os-tmpdir/1.0.2:
resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -4030,14 +3800,6 @@ packages:
aggregate-error: 3.1.0 aggregate-error: 3.1.0
dev: true dev: true
/p-retry/4.6.2:
resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==}
engines: {node: '>=8'}
dependencies:
'@types/retry': 0.12.0
retry: 0.13.1
dev: true
/p-try/2.2.0: /p-try/2.2.0:
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -4106,6 +3868,11 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dev: true dev: true
/path-key/4.0.0:
resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
engines: {node: '>=12'}
dev: true
/path-parse/1.0.7: /path-parse/1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
dev: true dev: true
@ -4449,11 +4216,6 @@ packages:
engines: {node: '>= 4'} engines: {node: '>= 4'}
dev: true dev: true
/retry/0.13.1:
resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==}
engines: {node: '>= 4'}
dev: true
/reusify/1.0.4: /reusify/1.0.4:
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'} engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
@ -4619,13 +4381,6 @@ packages:
source-map: 0.6.1 source-map: 0.6.1
dev: true dev: true
/source-map-support/0.5.21:
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
dependencies:
buffer-from: 1.1.2
source-map: 0.6.1
dev: true
/source-map/0.6.1: /source-map/0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -4766,6 +4521,11 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
dev: true dev: true
/strip-final-newline/3.0.0:
resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
engines: {node: '>=12'}
dev: true
/strip-indent/1.0.1: /strip-indent/1.0.1:
resolution: {integrity: sha512-I5iQq6aFMM62fBEAIB/hXzwJD6EEZ0xEGCX2t7oXqaKPIRgt4WruAQ285BISgdkP+HLGWyeGmNJcpIwFeRYRUA==} resolution: {integrity: sha512-I5iQq6aFMM62fBEAIB/hXzwJD6EEZ0xEGCX2t7oXqaKPIRgt4WruAQ285BISgdkP+HLGWyeGmNJcpIwFeRYRUA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -5014,12 +4774,41 @@ packages:
engines: {node: '>=6.10'} engines: {node: '>=6.10'}
dev: true dev: true
/tslib/1.14.1: /ts-jest/29.0.3_r24ewcothphvclnu77pxb4u4se:
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} resolution: {integrity: sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
hasBin: true
peerDependencies:
'@babel/core': '>=7.0.0-beta.0 <8'
'@jest/types': ^29.0.0
babel-jest: ^29.0.0
esbuild: '*'
jest: ^29.0.0
typescript: '>=4.3'
peerDependenciesMeta:
'@babel/core':
optional: true
'@jest/types':
optional: true
babel-jest:
optional: true
esbuild:
optional: true
dependencies:
bs-logger: 0.2.6
fast-json-stable-stringify: 2.1.0
jest: 29.3.1_@types+node@18.11.9
jest-util: 29.3.1
json5: 2.2.1
lodash.memoize: 4.1.2
make-error: 1.3.6
semver: 7.3.7
typescript: 4.8.4
yargs-parser: 21.0.1
dev: true dev: true
/tslib/2.4.1: /tslib/1.14.1:
resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
dev: true dev: true
/tty-table/4.1.6: /tty-table/4.1.6:

76
src/commands/env.rs

@ -6,6 +6,7 @@ use crate::outln;
use crate::path_ext::PathExt; use crate::path_ext::PathExt;
use crate::shell::{infer_shell, Shell, AVAILABLE_SHELLS}; use crate::shell::{infer_shell, Shell, AVAILABLE_SHELLS};
use colored::Colorize; use colored::Colorize;
use std::collections::HashMap;
use std::fmt::Debug; use std::fmt::Debug;
use thiserror::Error; use thiserror::Error;
@ -15,6 +16,9 @@ pub struct Env {
#[clap(long)] #[clap(long)]
#[clap(possible_values = AVAILABLE_SHELLS)] #[clap(possible_values = AVAILABLE_SHELLS)]
shell: Option<Box<dyn Shell>>, shell: Option<Box<dyn Shell>>,
/// Print JSON instead of shell commands.
#[clap(long, conflicts_with = "shell")]
json: bool,
/// Deprecated. This is the default now. /// Deprecated. This is the default now.
#[clap(long, hide = true)] #[clap(long, hide = true)]
multi: bool, multi: bool,
@ -59,50 +63,60 @@ impl Command for Env {
); );
} }
let shell: Box<dyn Shell> = self
.shell
.or_else(infer_shell)
.ok_or(Error::CantInferShell)?;
let multishell_path = make_symlink(config)?; let multishell_path = make_symlink(config)?;
let binary_path = if cfg!(windows) { let binary_path = if cfg!(windows) {
multishell_path.clone() multishell_path.clone()
} else { } else {
multishell_path.join("bin") multishell_path.join("bin")
}; };
println!("{}", shell.path(&binary_path)?);
println!( let env_vars = HashMap::from([
"{}", (
shell.set_env_var("FNM_MULTISHELL_PATH", multishell_path.to_str().unwrap()) "FNM_MULTISHELL_PATH",
); multishell_path.to_str().unwrap().to_owned(),
println!( ),
"{}", (
shell.set_env_var(
"FNM_VERSION_FILE_STRATEGY", "FNM_VERSION_FILE_STRATEGY",
config.version_file_strategy().as_str() config.version_file_strategy().as_str().to_owned(),
) ),
); (
println!( "FNM_DIR",
"{}", config.base_dir_with_default().to_str().unwrap().to_owned(),
shell.set_env_var("FNM_DIR", config.base_dir_with_default().to_str().unwrap()) ),
); (
println!( "FNM_LOGLEVEL",
"{}", <&'static str>::from(config.log_level().clone()).to_owned(),
shell.set_env_var("FNM_LOGLEVEL", config.log_level().clone().into()) ),
); (
println!( "FNM_NODE_DIST_MIRROR",
"{}", config.node_dist_mirror.as_str().to_owned(),
shell.set_env_var("FNM_NODE_DIST_MIRROR", config.node_dist_mirror.as_str()) ),
); ("FNM_ARCH", config.arch.to_string()),
println!( ]);
"{}",
shell.set_env_var("FNM_ARCH", &config.arch.to_string()) if self.json {
); println!("{}", serde_json::to_string(&env_vars).unwrap());
return Ok(());
}
let shell: Box<dyn Shell> = self
.shell
.or_else(infer_shell)
.ok_or(Error::CantInferShell)?;
println!("{}", shell.path(&binary_path)?);
for (name, value) in &env_vars {
println!("{}", shell.set_env_var(name, value));
}
if self.use_on_cd { if self.use_on_cd {
println!("{}", shell.use_on_cd(config)?); println!("{}", shell.use_on_cd(config)?);
} }
if let Some(v) = shell.rehash() { if let Some(v) = shell.rehash() {
println!("{}", v); println!("{}", v);
} }
Ok(()) Ok(())
} }
} }

4
tsconfig.json

@ -1,7 +1,9 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "esnext", "target": "esnext",
"module": "commonjs", "moduleResolution": "NodeNext",
"moduleDetection": "force",
"module": "esnext",
"esModuleInterop": true, "esModuleInterop": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,

Loading…
Cancel
Save