From 7ed5e1aaec90cdbacf986fc719cb93b5abf784ae Mon Sep 17 00:00:00 2001 From: Cass Fridkin Date: Tue, 21 Jun 2022 12:06:28 -0500 Subject: [PATCH] Delegate to local installs of wrangler when possible (#1270) Delegate to a local install of `wrangler` if one exists. Users will frequently install `wrangler` globally to run commands like `wrangler init`, but we also recommend pinning a specific version of `wrangler` in a project's `package.json`. Now, when a user invokes a global install of `wrangler`, we'll check to see if they also have a local installation. If they do, we'll delegate to that version. This requires that we define a `main` (maybe `module` would work too?) field in `package.json` so that `require.resolve` can find `"wrangler"`, but it's something of an implementation detail. --- .changeset/lovely-toys-happen.md | 7 ++++ packages/wrangler/bin/wrangler.js | 65 +++++++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 .changeset/lovely-toys-happen.md diff --git a/.changeset/lovely-toys-happen.md b/.changeset/lovely-toys-happen.md new file mode 100644 index 000000000000..0cb29216755d --- /dev/null +++ b/.changeset/lovely-toys-happen.md @@ -0,0 +1,7 @@ +--- +"wrangler": patch +--- + +Delegate to a local install of `wrangler` if one exists. + +Users will frequently install `wrangler` globally to run commands like `wrangler init`, but we also recommend pinning a specific version of `wrangler` in a project's `package.json`. Now, when a user invokes a global install of `wrangler`, we'll check to see if they also have a local installation. If they do, we'll delegate to that version. diff --git a/packages/wrangler/bin/wrangler.js b/packages/wrangler/bin/wrangler.js index 52d69a723754..b7bdc093c49c 100755 --- a/packages/wrangler/bin/wrangler.js +++ b/packages/wrangler/bin/wrangler.js @@ -9,7 +9,10 @@ const MIN_NODE_VERSION = "16.7.0"; let wranglerProcess; -async function main() { +/** + * Executes ../wrangler-dist/cli.js + */ +function runWrangler() { if (semiver(process.versions.node, MIN_NODE_VERSION) < 0) { // Note Volta and nvm are also recommended in the official docs: // https://developers.cloudflare.com/workers/get-started/guide#2-install-the-workers-cli @@ -46,7 +49,7 @@ Consider using a Node.js version manager such as https://volta.sh/ or https://gi pathToCACerts = certPath; } - wranglerProcess = spawn( + return spawn( process.execPath, [ ...(semiver(process.versions.node, "18.0.0") >= 0 @@ -70,11 +73,65 @@ Consider using a Node.js version manager such as https://volta.sh/ or https://gi ); } +/** + * Runs a locally-installed version of wrangler, delegating from this version. + * @throws {MODULE_NOT_FOUND} if there isn't a locally installed version of wrangler. + */ +function runDelegatedWrangler() { + const packageJsonPath = require.resolve("wrangler/package.json", { + paths: [process.cwd()], + }); + const { + bin: { wrangler: binaryPath }, + version, + } = JSON.parse(fs.readFileSync(packageJsonPath)); + const resolvedBinaryPath = path.resolve(packageJsonPath, "..", binaryPath); + + console.log( + `Delegating to locally-installed version of wrangler @ v${version}` + ); + // this call to `spawn` is simpler because the delegated version will do all + // of the other work. + return spawn( + process.execPath, + [resolvedBinaryPath, ...process.argv.slice(2)], + { + stdio: "inherit", + } + ).on("exit", (code) => + process.exit(code === undefined || code === null ? 0 : code) + ); +} + +/** + * Indicates if this invocation of `wrangler` should delegate + * to a locally-installed version. + */ +function shouldDelegate() { + try { + // `require.resolve` will throw if it can't find + // a locally-installed version of `wrangler` + const delegatedPackageJson = require.resolve("wrangler/package.json", { + paths: [process.cwd()], + }); + const thisPackageJson = path.resolve(__dirname, "..", "package.json"); + // if it's the same path, then we're already a local install -- no need to delegate + return thisPackageJson !== delegatedPackageJson; + } catch (e) { + // there's no local version to delegate to -- `require.resolve` threw + return false; + } +} + +async function main() { + wranglerProcess = shouldDelegate() ? runDelegatedWrangler() : runWrangler(); +} + process.on("SIGINT", () => { - wranglerProcess.kill(); + wranglerProcess?.kill(); }); process.on("SIGTERM", () => { - wranglerProcess.kill(); + wranglerProcess?.kill(); }); void main();