perf(wrangler): remove execa - #12369
Conversation
🦋 Changeset detectedLatest commit: c47d183 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@cloudflare/autoconfig
@cloudflare/build-output-utils
@cloudflare/config
create-cloudflare
@cloudflare/deploy-helpers
@cloudflare/kv-asset-handler
miniflare
@cloudflare/pages-functions
@cloudflare/pages-shared
@cloudflare/unenv-preset
@cloudflare/vite-plugin
@cloudflare/vitest-pool-workers
@cloudflare/workers-auth
@cloudflare/workers-editor-shared
@cloudflare/workers-utils
wrangler
commit: |
3723c61 to
56b361e
Compare
|
The windows CI job appears to keep hanging but also this needs another rebase. |
|
Strange. When I'm next on a Windows machine I can try run them locally and see what happens if that helps |
56b361e to
9499795
Compare
40277f9 to
5f1a98a
Compare
|
Hi @43081j — we're doing some cleanup of stale draft PRs. This PR has been quiet since 2026-02-13. Could you let us know within 14 days whether you're still planning to continue?
Thanks! |
workers-devprod
left a comment
There was a problem hiding this comment.
Codeowners reviews satisfied
|
Codeowners approval required for this PR:
Show detailed file reviewers |
|
@petebacondarwin i've revived it, thanks for the ping as i also forgot about this one. assuming CI is happy, let me know if there's any changes you or any other maintainers want |
|
Codeowners approval required for this PR:
Show detailed file reviewers |
|
Codeowners approval required for this PR:
Show detailed file reviewers |
|
@jamesopstad other than that seemingly stuck windows task, it builds! looks like it is just stuck building for some reason |
50e7e94 to
8929563
Compare
|
Codeowners approval required for this PR:
Show detailed file reviewers |
8929563 to
fe2f069
Compare
|
Rebased this onto latest Conflict resolution
Behavioural gaps between execa and tinyexec
Other
Not addressed — worth a follow-up execa v6 defaulted to Verification: One thing worth adding to the changeset if you like: |
fe2f069 to
590c8f5
Compare
this is a bug and was fixed in tinylibs/tinyexec#146 which should be in the next tinyexec release.
This may be solved by tinylibs/tinyexec#151
i do wonder if there is a simpler way, knowing exactly what we've spawned and the cases in which it can be left dangling. worth some thought instead of trying to port a "lazy" solution like that i think edit: 1.3.0 is out which should fix those two 👀 |
Two behavioural differences surfaced while migrating: - `throwOnError` only covers non-zero exit codes, so a process killed by a signal resolved successfully where execa rejected. For custom builds this silently turned an aborted build into a successful one. Guard all three call sites on a missing exit code. - `NonZeroExitError.message` does not name the command the way execa's `shortMessage` did. Embedding C3's captured stdout/stderr to compensate printed the output twice (it is already streamed live) and gave every failure a unique message, defeating Sentry grouping. Name the command and exit code instead, and let the output travel on the cause. Also narrows the C3 error with `instanceof` rather than an unchecked cast, trims the custom build command before handing it to the shell (`cmd.exe` is not whitespace tolerant, and `execaCommand` used to normalise this), and adds coverage for the abort, signal and C3 failure paths.
590c8f5 to
c47d183
Compare
|
Codeowners approval required for this PR:
Show detailed file reviewers |
|
We recently landed #14994, a large change to Miniflare's configuration internals that touched ~177 files across the repo. Leaving this PR on its old base was likely to cause conflicts, so we've rebased it onto the latest Your local copy of this branch is now out of date. Before you push again, please reset to the new version: git fetch origin
git checkout execaless
git reset --hard origin/execalessBecause the base moved a long way, it's also worth reinstalling before you carry on — the lockfile changed: pnpm installSorry for the interruption. If the rebase looks wrong, or CI now fails in a way that seems related to the Miniflare config change rather than your own work, comment here and we'll help get it sorted. |
| const { exitCode } = await x(name, ["--version"], { | ||
| nodeOptions: { stdio: "ignore" }, | ||
| // Disable tinyexec's default PATH manipulation, which prepends every | ||
| // ancestor `node_modules/.bin` and the directory holding the running Node | ||
| // binary, so that we only detect package managers that are actually | ||
| // available on the user's own PATH. | ||
| nodePath: false, | ||
| }); |
There was a problem hiding this comment.
🔴 Package managers may no longer be found on Windows, breaking project creation
Package managers are now looked up by launching them directly (x(name, ["--version"], …) at packages/wrangler/src/package-manager.ts:89) instead of through the previous helper that knew how to start Windows command shims, so on Windows npm/pnpm/yarn can appear to be missing.
Impact: Windows users may see "Unable to find a package manager" and wrangler init can fail to start the project-creation tool.
Mechanism: execa used cross-spawn for Windows `.cmd` shims, tinyexec spawns directly
execa@6 depends on cross-spawn, which exists precisely to make spawn("npm", …) work on Windows, where npm/pnpm/yarn are npm.cmd shims. libuv's PATH search only appends .com and .exe, so a bare spawn("npm") without shell: true fails with ENOENT (and Node rejects an explicit .cmd target without shell). tinyexec calls node:child_process.spawn directly and does not perform cross-spawn-style shim resolution.
Two call sites are affected:
packages/wrangler/src/package-manager.ts:89-96— a spawn failure is swallowed by thecatchand reported as "not supported", so every detection returnsfalseandgetPackageManager()throws the "Unable to find a package manager"UserError.packages/wrangler/src/init.ts:139—x(packageManager.type, c3Arguments, …)spawns the package manager binary directly to delegate to C3.
The existing Windows test path exercises exactly this: packages/wrangler/src/__tests__/helpers/mock-bin.ts:22-27 creates the mock binaries as cmd-shims on win32.
Note that runCommand in run-custom-build.ts is unaffected because it passes shell: true.
Prompt for agents
On Windows, `npm`, `pnpm` and `yarn` are `.cmd` shims. `execa` (the previous implementation) depended on `cross-spawn`, which resolves and runs those shims correctly; `tinyexec` calls `node:child_process.spawn` directly, and libuv's PATH search only tries `.com`/`.exe`, so `x("npm", ["--version"])` fails with ENOENT on Windows. Verify tinyexec@1.2.4's behaviour here (run the Windows unit tests for `packages/wrangler/src/__tests__/package-manager.test.ts`, which install cmd-shims via `helpers/mock-bin.ts`). If it does not resolve shims, both `supports()` in packages/wrangler/src/package-manager.ts and the C3 delegation spawn in packages/wrangler/src/init.ts need a Windows-safe strategy (e.g. keeping cross-spawn for these two call sites, appending `.cmd` on win32, or passing `shell: true` with properly escaped arguments).
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Tinyexec does the same work as cross-spawn under the hood, so this comment doesn't seem right 👀
It too handles windows specific resolution of binaries
This removes
execaand uses the much smaller, more moderntinyexecinstead.
Reasoning:
execa640KB vstinyexec26KBexeca23 packages vstinyexec1 packagetinyexecis widely adopted by most of the modern CLIs today (tsdown,vite, vitest, storybook, etc.)
Part of #11854.