Skip to content

perf: add TSGO_BINARY env var and fast-path sibling check in getExePath#3451

Open
nileshpatil6 wants to merge 2 commits intomicrosoft:mainfrom
nileshpatil6:fix/getexepath-resolve-perf
Open

perf: add TSGO_BINARY env var and fast-path sibling check in getExePath#3451
nileshpatil6 wants to merge 2 commits intomicrosoft:mainfrom
nileshpatil6:fix/getexepath-resolve-perf

Conversation

@nileshpatil6
Copy link
Copy Markdown

Fixes the performance issue raised in #2836 where import.meta.resolve is called on every separate tsgo invocation in large monorepos.

Two changes in getExePath.js:

1. TSGO_BINARY environment variable

If set, the function returns that path immediately and skips all resolution. This is useful for monorepo setups where users want to hardcode the binary location (e.g. in their shell config or CI environment). It also works as a diagnostic tool to verify whether path resolution is the actual bottleneck.

TSGO_BINARY=/path/to/tsgo npx tsgo ...

If the path does not exist, a clear error is thrown rather than silently failing.

2. Fast-path sibling check before import.meta.resolve

In the installed package branch, the code now checks if the platform binary exists at the predictable sibling location (../../{expectedPackage}/lib/) using a single fs.existsSync call before falling back to import.meta.resolve. This covers the standard npm/yarn layout where both @typescript/native-preview and @typescript/native-preview-{platform}-{arch} sit side by side under node_modules/@typescript/. If the sibling is not found there (e.g. unusual hoisting), it falls through to the existing import.meta.resolve logic unchanged.

The path calculation is the same as the one already used in the built/npm branch, so it is not new math.

Testing

I wrote a local test suite covering these cases before opening this PR:

  • TSGO_BINARY pointing to a real file returns it correctly
  • TSGO_BINARY pointing to a missing file throws with a clear message
  • Sibling path math resolves to the correct absolute path given a simulated node_modules/@typescript/native-preview/lib directory
  • Sibling exe found via fast path when the fake binary is present
  • Fast path absent falls through cleanly (no error from the fast path itself)
  • Source path (/_packages/native-preview/lib) detection still works
  • Built-npm path (/built/npm/native-preview/lib) detection still works

All 7 tests passed on Node 22.

Copilot AI review requested due to automatic review settings April 20, 2026 21:09
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves tsgo startup performance in monorepos by avoiding expensive module resolution in the native-preview launcher’s executable path lookup.

Changes:

  • Add TSGO_BINARY env var override to bypass resolution and return a user-specified executable path.
  • Add a fast-path sibling lookup for the platform package (@typescript/native-preview-{platform}-{arch}) before falling back to import.meta.resolve.

if (envPath) {
if (!fs.existsSync(envPath)) {
throw new Error("TSGO_BINARY points to non-existent file: " + envPath);
}
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TSGO_BINARY validation only checks fs.existsSync(envPath), but the error says it points to a non-existent file. existsSync also returns true for directories, so a directory (or other non-file) value will bypass this check and then fail later with a less clear exec error. Consider either validating that the path is a regular file (and possibly executable) or adjusting the error message to match what’s actually being checked.

Suggested change
}
}
const envStat = fs.statSync(envPath);
if (!envStat.isFile()) {
throw new Error("TSGO_BINARY does not point to a file: " + envPath);
}

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +15
const envPath = process.env.TSGO_BINARY;
if (envPath) {
if (!fs.existsSync(envPath)) {
throw new Error("TSGO_BINARY points to non-existent file: " + envPath);
}
return envPath;
}
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Windows, the normal resolution path applies a \\?\ long-path prefix when the resolved executable path is long. The TSGO_BINARY fast-path returns the env var value verbatim, so it can reintroduce long-path failures for the same setups this code is trying to support. Consider applying the same Windows long-path handling (and any other normalization you rely on later) before returning envPath.

Copilot uses AI. Check for mistakes.
@jakebailey
Copy link
Copy Markdown
Member

If someone has this path, why wouldn't they just run it directly? This seems really special case-y.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants