You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The Pack documentation advertises standalone executable builds as a supported feature, but using it requires non-obvious workarounds that contradict or go beyond what the docs describe. The root cause is a cascading package resolution problem: @tsdown/exe has a hard peer dependency on tsdown, but Vite+ bundles tsdown internally under @voidzero-dev/vite-plus-core and doesn't expose it as a resolvable top-level package.
While the bug here might better be resolved in the tsdown repository, I'm raising it as an issue here because of the confusing documentation and to flag it as an interop concern so that you can adjust your strategy during this early stage of Vite+'s development. Resolution issues like this could easily pop up in other places within the Vite+ toolchain.
Note
In general, I have observed that LLMs in particular have issues with the way Vite+ bundles together all of these tools, not just tsdown. Claude for example often goes down rabbit holes attempting to resolve particular packages like vitest. Numerous times I've had to teach it workarounds because of this architectural decision. Personally, I don't think it's a bad idea, I just want to report what I have observed is a frequent pain point.
A related example can be observed trying to use @tsdown/css. Moving forward it may be worth investigating how to better evolve these tools together. Food for thought.
What I Tried
Adding "tsdown": "npm:vite-plus@latest" to resolutions — vite-plus does expose ./pack and ./internal subpaths, but its ./internal export is Vite's internal module, not tsdown's. The tsdown/internal subpath required by @tsdown/exe exports different symbols (fsExists, fsRemove, Logger) that are not present in vite-plus/internal.
Adding "tsdown": "0.22.0" to resolutions — Yarn 4's resolutions only overrides packages already in the dependency graph. Since no explicit dependency declares tsdown (it's an unsatisfied peer of @tsdown/exe), the resolution is never applied and tsdown is never installed at the top level.
Using packageExtensions in .yarnrc.yml to mark the peer as optional — This silenced the peer warning but didn't make tsdown resolvable at runtime.
Working workaround: Use yarn patch to modify @tsdown/exe's dist, replacing the tsdown/internal import with inline node:fs/promises equivalents:
This works but is fragile — it will break on any @tsdown/exe version bump and requires manually re-running yarn patch after each update.
Suggested Fix
In order of preference:
Export tsdown/internal from vite-plus — add a "./tsdown/internal" (or "./pack/internal") subpath export to vite-plus that re-exports fsExists, fsRemove, and Logger, similar to how vite-plus/pack/client was added as a tsdown/client equivalent in v0.1.21 (#1501). Then document that "tsdown": "npm:vite-plus@latest" in resolutions satisfies the peer.
Bundle @tsdown/exe inside vite-plus-core — the same way tsdown itself is bundled, so users don't need to install or manage @tsdown/exe at all. This would match the zero-config philosophy and eliminate the peer dependency chain entirely.
Document the workaround explicitly — at minimum, the pack docs should warn that @tsdown/exe has a tsdown peer that isn't satisfied by Vite+'s internal bundling, and show the steps needed to work around it for each package manager.
I have linked to the repo this occurred in on the commit with the included yarn patch.
To reproduce, open a terminal in the sandbox and run vp pack. Note: the .tar.xz extraction failure seen on Windows (missing xz) will not occur in this environment, but the initial Failed to import module "@tsdown/exe" error will.
Install @tsdown/exe as a dev dependency per tsdown's documentation
Run vp pack
Expected: Executables are built for each target platform.
Actual:
error: Failed to import module "@tsdown/exe". Please ensure it is installed.
This error is misleading — @tsdown/exeis installed. The real failure happens when @tsdown/exe tries to import { fsExists, fsRemove } from "tsdown/internal". Because Vite+ bundles tsdown inside @voidzero-dev/vite-plus-core's dist rather than exposing it as a standalone tsdown package, the import cannot be resolved.
Full error output and stack trace:
ℹ entry: src/cli.ts
ℹ target: node26.1.0
ℹ tsconfig: tsconfig.json
ℹ `exe` option is experimental and may change in future releases.
ℹ Build start
ℹ dist/cli.mjs 35.99 kB │ gzip: 9.86 kB
ℹ 1 files, total: 35.99 kB
✔ Build complete in 38ms
error: Failed to import module "@tsdown/exe". Please ensure it is installed.
at importWithError (file:///path/to/project/node_modules/@voidzero-dev/vite-plus-core/dist/tsdown/main-BNUO6D3N.js:49:9)
at async buildExe (file:///path/to/project/node_modules/@voidzero-dev/vite-plus-core/dist/tsdown/build-5FURNVr0-C8s5A3Ji.js:3493:50)
at async postBuild (file:///path/to/project/node_modules/@voidzero-dev/vite-plus-core/dist/tsdown/build-5FURNVr0-C8s5A3Ji.js:5398:3)
at async buildSingle (file:///path/to/project/node_modules/@voidzero-dev/vite-plus-core/dist/tsdown/build-5FURNVr0-C8s5A3Ji.js:5331:3)
at async Promise.all (index 0)
at async buildWithConfigs (file:///path/to/project/node_modules/@voidzero-dev/vite-plus-core/dist/tsdown/build-5FURNVr0-C8s5A3Ji.js:5282:18)
at async runBuild (file:///path/to/project/node_modules/vite-plus/dist/pack-bin.js:672:3)
at async CAC.<anonymous> (file:///path/to/project/node_modules/vite-plus/dist/pack-bin.js:674:2)
at async runCLI (file:///path/to/project/node_modules/vite-plus/dist/pack-bin.js:680:3)
at async file:///path/to/project/node_modules/vite-plus/dist/pack-bin.js:687:1
The underlying cause is visible when importing @tsdown/exe directly:
Describe the bug
Summary
The Pack documentation advertises standalone executable builds as a supported feature, but using it requires non-obvious workarounds that contradict or go beyond what the docs describe. The root cause is a cascading package resolution problem:
@tsdown/exehas a hard peer dependency ontsdown, but Vite+ bundles tsdown internally under@voidzero-dev/vite-plus-coreand doesn't expose it as a resolvable top-level package.While the bug here might better be resolved in the tsdown repository, I'm raising it as an issue here because of the confusing documentation and to flag it as an interop concern so that you can adjust your strategy during this early stage of Vite+'s development. Resolution issues like this could easily pop up in other places within the Vite+ toolchain.
Note
In general, I have observed that LLMs in particular have issues with the way Vite+ bundles together all of these tools, not just tsdown. Claude for example often goes down rabbit holes attempting to resolve particular packages like vitest. Numerous times I've had to teach it workarounds because of this architectural decision. Personally, I don't think it's a bad idea, I just want to report what I have observed is a frequent pain point.
A related example can be observed trying to use
@tsdown/css. Moving forward it may be worth investigating how to better evolve these tools together. Food for thought.What I Tried
Adding
"tsdown": "npm:vite-plus@latest"toresolutions—vite-plusdoes expose./packand./internalsubpaths, but its./internalexport is Vite's internal module, not tsdown's. Thetsdown/internalsubpath required by@tsdown/exeexports different symbols (fsExists,fsRemove,Logger) that are not present invite-plus/internal.Adding
"tsdown": "0.22.0"toresolutions— Yarn 4'sresolutionsonly overrides packages already in the dependency graph. Since no explicit dependency declarestsdown(it's an unsatisfied peer of@tsdown/exe), the resolution is never applied andtsdownis never installed at the top level.Using
packageExtensionsin.yarnrc.ymlto mark the peer as optional — This silenced the peer warning but didn't maketsdownresolvable at runtime.Working workaround: Use
yarn patchto modify@tsdown/exe's dist, replacing thetsdown/internalimport with inlinenode:fs/promisesequivalents:This works but is fragile — it will break on any
@tsdown/exeversion bump and requires manually re-runningyarn patchafter each update.Suggested Fix
In order of preference:
Export
tsdown/internalfromvite-plus— add a"./tsdown/internal"(or"./pack/internal") subpath export tovite-plusthat re-exportsfsExists,fsRemove, andLogger, similar to howvite-plus/pack/clientwas added as atsdown/clientequivalent in v0.1.21 (#1501). Then document that"tsdown": "npm:vite-plus@latest"inresolutionssatisfies the peer.Bundle
@tsdown/exeinsidevite-plus-core— the same way tsdown itself is bundled, so users don't need to install or manage@tsdown/exeat all. This would match the zero-config philosophy and eliminate the peer dependency chain entirely.Document the workaround explicitly — at minimum, the pack docs should warn that
@tsdown/exehas atsdownpeer that isn't satisfied by Vite+'s internal bundling, and show the steps needed to work around it for each package manager.Reproduction
https://github.com/saeris/plex-monitor/tree/e25d8ea2096c040ad201ea51a47527e403b13cf6
Steps to reproduce
I have linked to the repo this occurred in on the commit with the included yarn patch.
To reproduce, open a terminal in the sandbox and run
vp pack. Note: the.tar.xzextraction failure seen on Windows (missingxz) will not occur in this environment, but the initialFailed to import module "@tsdown/exe"error will.Generalized reproduction steps:
execonfig tovite.config.tsas documented:@tsdown/exeas a dev dependency per tsdown's documentationvp packExpected: Executables are built for each target platform.
Actual:
This error is misleading —
@tsdown/exeis installed. The real failure happens when@tsdown/exetries toimport { fsExists, fsRemove } from "tsdown/internal". Because Vite+ bundles tsdown inside@voidzero-dev/vite-plus-core's dist rather than exposing it as a standalonetsdownpackage, the import cannot be resolved.Full error output and stack trace:
The underlying cause is visible when importing
@tsdown/exedirectly:The offending line in
@tsdown/exe/dist/index.mjs:System Info
Used Package Manager
yarn
Validations