diff --git a/package.json b/package.json index 687c1be..6dd36fc 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "bugs": "https://github.com/oclif/plugin-version/issues", "dependencies": { "@oclif/core": "^4", - "ansis": "^3.17.0" + "ansis": "^3.17.0", + "which": "^4" }, "devDependencies": { "@commitlint/config-conventional": "^19", @@ -17,6 +18,7 @@ "@types/chai": "^4.3.17", "@types/mocha": "^10", "@types/node": "^18", + "@types/which": "^3", "chai": "^4.5.0", "commitlint": "^19", "eslint": "^9.39.5", diff --git a/src/commands/version.ts b/src/commands/version.ts index f751eea..6295c82 100644 --- a/src/commands/version.ts +++ b/src/commands/version.ts @@ -2,6 +2,8 @@ import {Command, Flags, Interfaces} from '@oclif/core' import {Ansis} from 'ansis' import {execFile} from 'node:child_process' import {EOL} from 'node:os' +import {dirname, resolve} from 'node:path' +import which from 'which' const ansis = new Ansis() @@ -19,17 +21,25 @@ type NpmDetails = { } async function getNpmDetails(pkg: string): Promise { - return new Promise((resolve) => { - // Use execFile instead of exec to avoid shell interpretation. - // exec invokes cmd.exe on Windows, which resolves commands from CWD before PATH. - execFile('npm', ['view', pkg, '--json'], (error, stdout) => { + return new Promise((resolve_) => { + // Resolve npm from PATH only, excluding CWD. On Windows, both execFile (CreateProcess) + // and the `which` module resolve executables from CWD before PATH. + const cwd = resolve(process.cwd()) + const npmPath = which.sync('npm', {nothrow: true}) + if (!npmPath || dirname(resolve(npmPath)) === cwd) { + resolve_(false) + return + } + + const useShell = /\.(cmd|bat)$/i.test(npmPath) + execFile(npmPath, ['view', pkg, '--json'], {shell: useShell}, (error, stdout) => { if (error) { - resolve(false) + resolve_(false) } else { try { - resolve(JSON.parse(stdout) as NpmDetails) + resolve_(JSON.parse(stdout) as NpmDetails) } catch { - resolve(false) + resolve_(false) } } }) diff --git a/yarn.lock b/yarn.lock index 77a0aa0..f3eb1c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1196,6 +1196,11 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== +"@types/which@^3": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/which/-/which-3.0.4.tgz#2c3a89be70c56a84a6957a7264639f39ae4340a1" + integrity sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w== + "@types/wrap-ansi@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz#18b97a972f94f60a679fd5c796d96421b9abb9fd" @@ -4103,6 +4108,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +isexe@^3.1.1: + version "3.1.5" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-3.1.5.tgz#42e368f68d5e10dadfee4fda7b550bc2d8892dc9" + integrity sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w== + jackspeak@^3.1.2: version "3.4.3" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" @@ -6197,6 +6207,13 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +which@^4: + version "4.0.0" + resolved "https://registry.yarnpkg.com/which/-/which-4.0.0.tgz#cd60b5e74503a3fbcfbf6cd6b4138a8bae644c1a" + integrity sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg== + dependencies: + isexe "^3.1.1" + widest-line@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca"