Skip to content

Windows: extensionless shebang scripts fail when passed by path #160

Description

@sheremet-va

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions