Reproduction
On a Windows runner, install tinyexec@1.2.4 and run:
import { chmodSync, mkdtempSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { x } from 'tinyexec'
const dir = mkdtempSync(join(tmpdir(), 'tinyexec-'))
const script = join(dir, 'fake-tsc')
writeFileSync(script, '#!/usr/bin/env node\nconsole.log("ok")\n')
chmodSync(script, 0o755)
const result = await x(script, [], { throwOnError: false })
console.log(result)
Expected
The extensionless script is detected through its shebang and exits successfully with stdout: "ok\n", as it does with tinyexec@1.0.2.
Actual
With tinyexec@1.2.4, the command exits with code 1 without running the script. This broke Vitest's Windows test for custom typechecker executables when upgrading from 1.0.2.
The likely cause is resolveCommand: it only adds the empty extension when the command contains ., so an explicit extensionless path is never checked directly and the shebang detection is skipped:
https://github.com/tinylibs/tinyexec/blob/1.2.4/src/normalize.ts#L146-L150
Explicit paths could be checked without an extension before applying PATHEXT.
Environment
- Windows Server 2025 GitHub Actions runner
- Node.js 24
tinyexec@1.2.4
Reproduction
On a Windows runner, install
tinyexec@1.2.4and run:Expected
The extensionless script is detected through its shebang and exits successfully with
stdout: "ok\n", as it does withtinyexec@1.0.2.Actual
With
tinyexec@1.2.4, the command exits with code 1 without running the script. This broke Vitest's Windows test for custom typechecker executables when upgrading from 1.0.2.The likely cause is
resolveCommand: it only adds the empty extension when the command contains., so an explicit extensionless path is never checked directly and the shebang detection is skipped:https://github.com/tinylibs/tinyexec/blob/1.2.4/src/normalize.ts#L146-L150
Explicit paths could be checked without an extension before applying
PATHEXT.Environment
tinyexec@1.2.4