diff options
| author | 2023-04-28 01:36:44 +0800 | |
|---|---|---|
| committer | 2023-04-28 01:36:44 +0800 | |
| commit | dd84b9d64fb98746a230cd24233ff50a562c39c9 (patch) | |
| tree | b583261ef00b3afe72ec4d6dacb31e57779a6faf /packages/turbo | |
| parent | 0b46fcd72ac34382387b2bcf9095233efbcc52f4 (diff) | |
| download | HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.tar.gz HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.zip | |
Diffstat (limited to 'packages/turbo')
| -rw-r--r-- | packages/turbo/.dev-mode | 3 | ||||
| -rw-r--r-- | packages/turbo/README.md | 54 | ||||
| -rw-r--r-- | packages/turbo/bin/turbo | 14 | ||||
| -rw-r--r-- | packages/turbo/bump-version.js | 21 | ||||
| -rw-r--r-- | packages/turbo/install.js | 331 | ||||
| -rw-r--r-- | packages/turbo/node-platform.js | 257 | ||||
| -rw-r--r-- | packages/turbo/package.json | 29 |
7 files changed, 709 insertions, 0 deletions
diff --git a/packages/turbo/.dev-mode b/packages/turbo/.dev-mode new file mode 100644 index 0000000..c25642e --- /dev/null +++ b/packages/turbo/.dev-mode @@ -0,0 +1,3 @@ +DO NOT DELETE OR NPM PUBLISH THIS FILE. + +Its existence is used by `install.js` to determine if it should attempt to replace the binary with the native executable. diff --git a/packages/turbo/README.md b/packages/turbo/README.md new file mode 100644 index 0000000..97557bc --- /dev/null +++ b/packages/turbo/README.md @@ -0,0 +1,54 @@ +<p align="center"> + <a href="https://turbo.build/repo"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/4060187/196936123-f6e1db90-784d-4174-b774-92502b718836.png"> + <img src="https://user-images.githubusercontent.com/4060187/196936104-5797972c-ab10-4834-bd61-0d1e5f442c9c.png" height="128"> + </picture> + <h1 align="center">Turborepo</h1> + </a> +</p> + +<p align="center"> + <a aria-label="Vercel logo" href="https://vercel.com/"> + <img src="https://img.shields.io/badge/MADE%20BY%20Vercel-000000.svg?style=for-the-badge&logo=Vercel&labelColor=000"> + </a> + <a aria-label="NPM version" href="https://www.npmjs.com/package/turbo"> + <img alt="" src="https://img.shields.io/npm/v/turbo.svg?style=for-the-badge&labelColor=000000"> + </a> + <a aria-label="License" href="https://github.com/vercel/turbo/blob/main/LICENSE"> + <img alt="" src="https://img.shields.io/npm/l/turbo.svg?style=for-the-badge&labelColor=000000&color="> + </a> + <a aria-label="Join the community on GitHub" href="https://github.com/vercel/turbo/discussions"> + <img alt="" src="https://img.shields.io/badge/Join%20the%20community-blueviolet.svg?style=for-the-badge&logo=turborepo&labelColor=000000&logoWidth=20&logoColor=white"> + </a> +</p> + +## Getting Started + +Visit https://turbo.build/repo/docs to get started with Turborepo and read the documentation. + +## Community + +The Turborepo community can be found on [GitHub Discussions](https://github.com/vercel/turbo/discussions), where you can ask questions, voice ideas, and share your projects. + +To chat with other community members, you can join the [Turborepo Discord](https://turbo.build/discord) + +Our [Code of Conduct](https://github.com/vercel/turbo/blob/main/CODE_OF_CONDUCT.md) applies to all Turborepo community channels. + +## Who is using Turbo? + +Turbo is used by the world's leading companies. Check out the [Turbo Showcase](https://turbo.build/showcase) to learn more. + +## Updates + +Follow [@turborepo](https://twitter.com/turborepo) on Twitter and for project updates + +## Author + +- Jared Palmer ([@jaredpalmer](https://twitter.com/jaredpalmer)) + +## Security + +If you believe you have found a security vulnerability in Turbo, we encourage you to responsibly disclose this and not open a public issue. We will investigate all legitimate reports. Email `security@vercel.com` to disclose any security vulnerabilities. + +https://vercel.com/security diff --git a/packages/turbo/bin/turbo b/packages/turbo/bin/turbo new file mode 100644 index 0000000..d998261 --- /dev/null +++ b/packages/turbo/bin/turbo @@ -0,0 +1,14 @@ +#!/usr/bin/env node + +const { generateBinPath } = require("../node-platform"); + +try { + require("child_process").execFileSync( + generateBinPath(), + process.argv.slice(2), + { stdio: "inherit" } + ); +} catch(e) { + if(e && e.status) process.exit(e.status); + throw e; +} diff --git a/packages/turbo/bump-version.js b/packages/turbo/bump-version.js new file mode 100644 index 0000000..d3d48d1 --- /dev/null +++ b/packages/turbo/bump-version.js @@ -0,0 +1,21 @@ +#!/usr/bin/env node + +const fs = require("fs"); +const { + knownUnixlikePackages, + knownWindowsPackages, +} = require("./node-platform"); + +const pkg = require("./package.json"); +const file = require.resolve("./package.json"); + +pkg.optionalDependencies = Object.fromEntries( + Object.values({ + ...knownWindowsPackages, + ...knownUnixlikePackages, + }) + .sort() + .map((x) => [x, pkg.version]) +); + +fs.writeFileSync(file, JSON.stringify(pkg, null, 2) + "\n"); diff --git a/packages/turbo/install.js b/packages/turbo/install.js new file mode 100644 index 0000000..8b939c4 --- /dev/null +++ b/packages/turbo/install.js @@ -0,0 +1,331 @@ +// Most of this file is ripped from esbuild +// @see https://github.com/evanw/esbuild/blob/master/lib/npm/node-install.ts +// This file is MIT licensed. + +const nodePlatform = require("./node-platform"); +const fs = require("fs"); +const os = require("os"); +const path = require("path"); +const zlib = require("zlib"); +const https = require("https"); +const child_process = require("child_process"); +const { + downloadedBinPath, + TURBO_BINARY_PATH, + pkgAndSubpathForCurrentPlatform, +} = nodePlatform; +const TURBO_VERSION = require("./package.json").version; + +const toPath = path.join(__dirname, "bin", "turbo"); +const goToPath = path.join(__dirname, "bin", "go-turbo"); +let isToPathJS = true; + +function validateBinaryVersion(...command) { + command.push("--version"); + // Make sure that we get the version of the binary that was just installed + command.push("--skip-infer"); + const stdout = child_process + .execFileSync(command.shift(), command, { + // Without this, this install script strangely crashes with the error + // "EACCES: permission denied, write" but only on Ubuntu Linux when node is + // installed from the Snap Store. This is not a problem when you download + // the official version of node. The problem appears to be that stderr + // (i.e. file descriptor 2) isn't writable? + // + // More info: + // - https://snapcraft.io/ (what the Snap Store is) + // - https://nodejs.org/dist/ (download the official version of node) + // - https://github.com/evanw/esbuild/issues/1711#issuecomment-1027554035 + // + stdio: "pipe", + }) + .toString() + .trim(); + if (stdout !== TURBO_VERSION) { + throw new Error( + `Expected ${JSON.stringify(TURBO_VERSION)} but got ${JSON.stringify( + stdout + )}` + ); + } +} + +function isYarn() { + const { npm_config_user_agent } = process.env; + if (npm_config_user_agent) { + return /\byarn\//.test(npm_config_user_agent); + } + return false; +} + +function fetch(url) { + return new Promise((resolve, reject) => { + https + .get(url, (res) => { + if ( + (res.statusCode === 301 || res.statusCode === 302) && + res.headers.location + ) + return fetch(res.headers.location).then(resolve, reject); + if (res.statusCode !== 200) + return reject(new Error(`Server responded with ${res.statusCode}`)); + let chunks = []; + res.on("data", (chunk) => chunks.push(chunk)); + res.on("end", () => resolve(Buffer.concat(chunks))); + }) + .on("error", reject); + }); +} + +function extractFileFromTarGzip(buffer, subpath) { + try { + buffer = zlib.unzipSync(buffer); + } catch (err) { + throw new Error( + `Invalid gzip data in archive: ${(err && err.message) || err}` + ); + } + let str = (i, n) => + String.fromCharCode(...buffer.subarray(i, i + n)).replace(/\0.*$/, ""); + let offset = 0; + subpath = `package/${subpath}`; + while (offset < buffer.length) { + let name = str(offset, 100); + let size = parseInt(str(offset + 124, 12), 8); + offset += 512; + if (!isNaN(size)) { + if (name === subpath) return buffer.subarray(offset, offset + size); + offset += (size + 511) & ~511; + } + } + throw new Error(`Could not find ${JSON.stringify(subpath)} in archive`); +} + +function installUsingNPM(pkg, subpath, binPath) { + // Erase "npm_config_global" so that "npm install --global turbo" works. + // Otherwise this nested "npm install" will also be global, and the install + // will deadlock waiting for the global installation lock. + const env = { ...process.env, npm_config_global: undefined }; + + // Create a temporary directory inside the "turbo" package with an empty + // "package.json" file. We'll use this to run "npm install" in. + const turboLibDir = path.dirname(require.resolve("turbo")); + const installDir = path.join(turboLibDir, "npm-install"); + fs.mkdirSync(installDir); + try { + fs.writeFileSync(path.join(installDir, "package.json"), "{}"); + + // Run "npm install" in the temporary directory which should download the + // desired package. Try to avoid unnecessary log output. This uses the "npm" + // command instead of a HTTP request so that it hopefully works in situations + // where HTTP requests are blocked but the "npm" command still works due to, + // for example, a custom configured npm registry and special firewall rules. + child_process.execSync( + `npm install --loglevel=error --prefer-offline --no-audit --progress=false ${pkg}@${TURBO_VERSION}`, + { cwd: installDir, stdio: "pipe", env } + ); + + // Move the downloaded binary executable into place. The destination path + // is the same one that the JavaScript API code uses so it will be able to + // find the binary executable here later. + const installedBinPath = path.join( + installDir, + "node_modules", + pkg, + subpath + ); + fs.renameSync(installedBinPath, binPath); + } finally { + // Try to clean up afterward so we don't unnecessarily waste file system + // space. Leaving nested "node_modules" directories can also be problematic + // for certain tools that scan over the file tree and expect it to have a + // certain structure. + try { + removeRecursive(installDir); + } catch { + // Removing a file or directory can randomly break on Windows, returning + // EBUSY for an arbitrary length of time. I think this happens when some + // other program has that file or directory open (e.g. an anti-virus + // program). This is fine on Unix because the OS just unlinks the entry + // but keeps the reference around until it's unused. There's nothing we + // can do in this case so we just leave the directory there. + } + } +} + +function removeRecursive(dir) { + for (const entry of fs.readdirSync(dir)) { + const entryPath = path.join(dir, entry); + let stats; + try { + stats = fs.lstatSync(entryPath); + } catch { + continue; // Guard against https://github.com/nodejs/node/issues/4760 + } + if (stats.isDirectory()) removeRecursive(entryPath); + else fs.unlinkSync(entryPath); + } + fs.rmSync(dir); +} + +function maybeOptimizePackage(binPath) { + // Everything else that this installation does is fine, but the optimization + // step rewrites existing files. We need to make sure that this does not + // happen during development. We determine that by looking for a file in + // the package that is not published in the `npm` registry. + if (fs.existsSync(path.join(__dirname, ".dev-mode"))) { + return; + } + + // This package contains a "bin/turbo" JavaScript file that finds and runs + // the appropriate binary executable. However, this means that running the + // "turbo" command runs another instance of "node" which is way slower than + // just running the binary executable directly. + // + // Here we optimize for this by replacing the JavaScript file with the binary + // executable at install time. This optimization does not work on Windows + // because on Windows the binary executable must be called "turbo.exe" + // instead of "turbo". + // + // This also doesn't work with Yarn both because of lack of support for binary + // files in Yarn 2+ (see https://github.com/yarnpkg/berry/issues/882) and + // because Yarn (even Yarn 1?) may run the same install scripts in the same + // place multiple times from different platforms, especially when people use + // Docker. Avoid idempotency issues by just not optimizing when using Yarn. + // + // This optimization also doesn't apply when npm's "--ignore-scripts" flag is + // used since in that case this install script will not be run. + if (os.platform() !== "win32" && !isYarn()) { + const optimizeBin = (from, to, temp) => { + const tempPath = path.join(__dirname, temp); + try { + // First link the binary with a temporary file. If this fails and throws an + // error, then we'll just end up doing nothing. This uses a hard link to + // avoid taking up additional space on the file system. + fs.linkSync(from, tempPath); + + // Then use rename to atomically replace the target file with the temporary + // file. If this fails and throws an error, then we'll just end up leaving + // the temporary file there, which is harmless. + fs.renameSync(tempPath, to); + + // If we get here, then we know that the target location is now a binary + // executable instead of a JavaScript file. + isToPathJS = false; + + // If this install script is being re-run, then "renameSync" will fail + // since the underlying inode is the same (it just returns without doing + // anything, and without throwing an error). In that case we should remove + // the file manually. + fs.unlinkSync(tempPath); + } catch { + // Ignore errors here since this optimization is optional + } + }; + const goBinPath = path.join(path.dirname(binPath), "go-turbo"); + optimizeBin(goBinPath, goToPath, "bin-go-turbo"); + optimizeBin(binPath, toPath, "bin-turbo"); + } +} + +async function downloadDirectlyFromNPM(pkg, subpath, binPath) { + // If that fails, the user could have npm configured incorrectly or could not + // have npm installed. Try downloading directly from npm as a last resort. + const url = `https://registry.npmjs.org/${pkg}/-/${pkg}-${TURBO_VERSION}.tgz`; + console.error(`[turbo] Trying to download ${JSON.stringify(url)}`); + try { + fs.writeFileSync( + binPath, + extractFileFromTarGzip(await fetch(url), subpath) + ); + fs.chmodSync(binPath, 0o755); + } catch (e) { + console.error( + `[turbo] Failed to download ${JSON.stringify(url)}: ${ + (e && e.message) || e + }` + ); + throw e; + } +} + +async function checkAndPreparePackage() { + // This feature was added to give external code a way to modify the binary + // path without modifying the code itself. Do not remove this because + // external code relies on this (in addition to turbo's own test suite). + if (TURBO_BINARY_PATH) { + return; + } + + const { pkg, subpath } = pkgAndSubpathForCurrentPlatform(); + + let binPath; + try { + // First check for the binary package from our "optionalDependencies". This + // package should have been installed alongside this package at install time. + binPath = require.resolve(`${pkg}/${subpath}`); + } catch (e) { + console.error(`[turbo] Failed to find package "${pkg}" on the file system + +This can happen if you use the "--no-optional" flag. The "optionalDependencies" +package.json feature is used by turbo to install the correct binary executable +for your current platform. This install script will now attempt to work around +this. If that fails, you need to remove the "--no-optional" flag to use turbo. +`); + + // If that didn't work, then someone probably installed turbo with the + // "--no-optional" flag. Attempt to compensate for this by downloading the + // package using a nested call to "npm" instead. + // + // THIS MAY NOT WORK. Package installation uses "optionalDependencies" for + // a reason: manually downloading the package has a lot of obscure edge + // cases that fail because people have customized their environment in + // some strange way that breaks downloading. This code path is just here + // to be helpful but it's not the supported way of installing turbo. + binPath = downloadedBinPath(pkg, subpath); + try { + console.error(`[turbo] Trying to install package "${pkg}" using npm`); + installUsingNPM(pkg, subpath, binPath); + } catch (e2) { + console.error( + `[turbo] Failed to install package "${pkg}" using npm: ${ + (e2 && e2.message) || e2 + }` + ); + + // If that didn't also work, then something is likely wrong with the "npm" + // command. Attempt to compensate for this by manually downloading the + // package from the npm registry over HTTP as a last resort. + try { + await downloadDirectlyFromNPM(pkg, subpath, binPath); + } catch (e3) { + throw new Error(`Failed to install package "${pkg}"`); + } + } + } + + maybeOptimizePackage(binPath); +} + +checkAndPreparePackage().then(() => { + try { + if (isToPathJS) { + // We need "node" before this command since it's a JavaScript file + validateBinaryVersion("node", toPath); + } else { + // This is no longer a JavaScript file so don't run it using "node" + validateBinaryVersion(toPath); + } + } catch (err) { + if ( + process.platform === "linux" && + err.message && + err.message.includes("ENOENT") + ) { + console.error( + `Error: Failed to run turbo binary, you may need to install glibc compat\nSee https://turbo.build/repo/docs/getting-started/existing-monorepo#install-turbo` + ); + } + throw err; + } +}); diff --git a/packages/turbo/node-platform.js b/packages/turbo/node-platform.js new file mode 100644 index 0000000..f65a662 --- /dev/null +++ b/packages/turbo/node-platform.js @@ -0,0 +1,257 @@ +// Most of this file is ripped from esbuild +// @see https://github.com/evanw/esbuild/blob/master/lib/npm/node-install.ts +// This file is MIT licensed. + +const fs = require("fs"); +const os = require("os"); +const path = require("path"); + +// This feature was added to give external code a way to modify the binary +// path without modifying the code itself. Do not remove this because +// external code relies on this. +const TURBO_BINARY_PATH = process.env.TURBO_BINARY_PATH; + +const packageDarwin_arm64 = "turbo-darwin-arm64"; +const packageDarwin_x64 = "turbo-darwin-64"; + +const knownWindowsPackages = { + "win32 arm64 LE": "turbo-windows-arm64", + "win32 x64 LE": "turbo-windows-64", +}; + +const knownUnixlikePackages = { + "darwin arm64 LE": packageDarwin_arm64, + "darwin x64 LE": packageDarwin_x64, + "linux arm64 LE": "turbo-linux-arm64", + "linux x64 LE": "turbo-linux-64", +}; + +function pkgAndSubpathForCurrentPlatform() { + let pkg; + let subpath; + let platformKey = `${process.platform} ${os.arch()} ${os.endianness()}`; + + if (platformKey in knownWindowsPackages) { + pkg = knownWindowsPackages[platformKey]; + subpath = "bin/turbo.exe"; + } else if (platformKey in knownUnixlikePackages) { + pkg = knownUnixlikePackages[platformKey]; + subpath = "bin/turbo"; + } else { + throw new Error(`Unsupported platform: ${platformKey}`); + } + return { pkg, subpath }; +} + +function downloadedBinPath(pkg, subpath) { + const turboLibDir = path.dirname(require.resolve("turbo")); + return path.join(turboLibDir, `downloaded-${pkg}-${path.basename(subpath)}`); +} + +function pkgForSomeOtherPlatform() { + const libMainJS = require.resolve("turbo"); + const nodeModulesDirectory = path.dirname( + path.dirname(path.dirname(libMainJS)) + ); + if (path.basename(nodeModulesDirectory) === "node_modules") { + for (const unixKey in knownUnixlikePackages) { + try { + const pkg = knownUnixlikePackages[unixKey]; + if (fs.existsSync(path.join(nodeModulesDirectory, pkg))) return pkg; + } catch {} + } + for (const windowsKey in knownWindowsPackages) { + try { + const pkg = knownWindowsPackages[windowsKey]; + if (fs.existsSync(path.join(nodeModulesDirectory, pkg))) return pkg; + } catch {} + } + } + return null; +} + +function generateBinPath() { + // This feature was added to give external code a way to modify the binary + // path without modifying the code itself. Do not remove this because + // external code relies on this (in addition to turbo's own test suite). + if (TURBO_BINARY_PATH) { + if (!fs.existsSync(TURBO_BINARY_PATH)) { + console.warn( + `[turbo] Ignoring bad configuration: TURBO_BINARY_PATH=${TURBO_BINARY_PATH}` + ); + } else { + return TURBO_BINARY_PATH; + } + } + + const { pkg, subpath } = pkgAndSubpathForCurrentPlatform(); + let binPath; + + try { + // First check for the binary package from our "optionalDependencies". This + // package should have been installed alongside this package at install time. + binPath = require.resolve(`${pkg}/${subpath}`); + } catch (e) { + // If that didn't work, then someone probably installed turbo with the + // "--no-optional" flag. Our install script attempts to compensate for this + // by manually downloading the package instead. Check for that next. + binPath = downloadedBinPath(pkg, subpath); + if (!fs.existsSync(binPath)) { + // If that didn't work too, check to see whether the package is even there + // at all. It may not be (for a few different reasons). + try { + require.resolve(pkg); + } catch { + // If we can't find the package for this platform, then it's possible + // that someone installed this for some other platform and is trying + // to use it without reinstalling. That won't work of course, but + // people do this all the time with systems like Docker. Try to be + // helpful in that case. + const otherPkg = pkgForSomeOtherPlatform(); + if (otherPkg) { + let suggestions = ` +Specifically the "${otherPkg}" package is present but this platform +needs the "${pkg}" package instead. People often get into this +situation by installing turbo on Windows or macOS and copying "node_modules" +into a Docker image that runs Linux, or by copying "node_modules" between +Windows and WSL environments. +If you are installing with npm, you can try not copying the "node_modules" +directory when you copy the files over, and running "npm ci" or "npm install" +on the destination platform after the copy. Or you could consider using yarn +instead of npm which has built-in support for installing a package on multiple +platforms simultaneously. +If you are installing with yarn, you can try listing both this platform and the +other platform in your ".yarnrc.yml" file using the "supportedArchitectures" +feature: https://yarnpkg.com/configuration/yarnrc/#supportedArchitectures +Keep in mind that this means multiple copies of turbo will be present. +`; + + // Use a custom message for macOS-specific architecture issues + if ( + (pkg === packageDarwin_x64 && otherPkg === packageDarwin_arm64) || + (pkg === packageDarwin_arm64 && otherPkg === packageDarwin_x64) + ) { + suggestions = ` +Specifically the "${otherPkg}" package is present but this platform +needs the "${pkg}" package instead. People often get into this +situation by installing turbo with npm running inside of Rosetta 2 and then +trying to use it with node running outside of Rosetta 2, or vice versa (Rosetta +2 is Apple's on-the-fly x86_64-to-arm64 translation service). +If you are installing with npm, you can try ensuring that both npm and node are +not running under Rosetta 2 and then reinstalling turbo. This likely involves +changing how you installed npm and/or node. For example, installing node with +the universal installer here should work: https://nodejs.org/en/download/. Or +you could consider using yarn instead of npm which has built-in support for +installing a package on multiple platforms simultaneously. +If you are installing with yarn, you can try listing both "arm64" and "x64" +in your ".yarnrc.yml" file using the "supportedArchitectures" feature: +https://yarnpkg.com/configuration/yarnrc/#supportedArchitectures +Keep in mind that this means multiple copies of turbo will be present. +`; + } + + throw new Error(` +You installed turbo for another platform than the one you're currently using. +This won't work because turbo is written with native code and needs to +install a platform-specific binary executable. +${suggestions} +Another alternative is to use the "turbo-wasm" package instead, which works +the same way on all platforms. But it comes with a heavy performance cost and +can sometimes be 10x slower than the "turbo" package, so you may also not +want to do that. +`); + } + + // If that didn't work too, then maybe someone installed turbo with + // both the "--no-optional" and the "--ignore-scripts" flags. The fix + // for this is to just not do that. We don't attempt to handle this + // case at all. + // + // In that case we try to have a nice error message if we think we know + // what's happening. Otherwise we just rethrow the original error message. + throw new Error(`The package "${pkg}" could not be found, and is needed by turbo. +If you are installing turbo with npm, make sure that you don't specify the +"--no-optional" or "--omit=optional" flags. The "optionalDependencies" feature +of "package.json" is used by turbo to install the correct binary executable +for your current platform.`); + } + throw e; + } + } + + // This code below guards against the unlikely case that the user is using + // Yarn 2+ in PnP mode and that version is old enough that it doesn't support + // the "preferUnplugged" setting. If that's the case, then the path to the + // binary executable that we got above isn't actually a real path. Instead + // it's a path to a zip file with some extra stuff appended to it. + // + // Yarn's PnP mode tries hard to patch Node's file system APIs to pretend + // that these fake paths are real. So we can't check whether it's a real file + // or not by using Node's file system APIs (e.g. "fs.existsSync") because + // they have been patched to lie. But we can't return this fake path because + // Yarn hasn't patched "child_process.execFileSync" to work with fake paths, + // so attempting to execute the binary will fail. + // + // As a hack, we use Node's file system APIs to copy the file from the fake + // path to a real path. This will cause Yarn's hacked file system to extract + // the binary executable from the zip file and turn it into a real file that + // we can execute. + // + // This is only done when both ".zip/" is present in the path and the + // "pnpapi" package is present, which is a strong indication that Yarn PnP is + // being used. There is no API at all for telling whether something is a real + // file or not as far as I can tell. Even Yarn's own code just checks for + // whether ".zip/" is present in the path or not. + // + // Note to self: One very hacky way to tell if a path is under the influence + // of Yarn's file system hacks is to stat the file and check for the "crc" + // property in the result. If that's present, then the file is inside a zip + // file. However, I haven't done that here because it's not intended to be + // used that way. Who knows what Yarn versions it does or does not work on + // (including future versions). + if (/\.zip\//.test(binPath)) { + let pnpapi; + try { + pnpapi = require("pnpapi"); + } catch (e) {} + if (pnpapi) { + // Copy the executable to ".cache/turbo". The official recommendation + // of the Yarn team is to use the directory "node_modules/.cache/turbo": + // https://yarnpkg.com/advanced/rulebook/#packages-should-never-write-inside-their-own-folder-outside-of-postinstall + // People that use Yarn in PnP mode find this really annoying because they + // don't like seeing the "node_modules" directory. These people should + // either work with Yarn to change their recommendation, or upgrade their + // version of Yarn, since newer versions of Yarn shouldn't stick turbo's + // binary executables in a zip file due to the "preferUnplugged" setting. + const root = pnpapi.getPackageInformation( + pnpapi.topLevel + ).packageLocation; + const binTargetPath = path.join( + root, + "node_modules", + ".cache", + "turbo", + `pnpapi-${pkg.replace("/", "-")}-${TURBO_VERSION}-${path.basename( + subpath + )}` + ); + if (!fs.existsSync(binTargetPath)) { + fs.mkdirSync(path.dirname(binTargetPath), { recursive: true }); + fs.copyFileSync(binPath, binTargetPath); + fs.chmodSync(binTargetPath, 0o755); + } + return binTargetPath; + } + } + + return binPath; +} + +module.exports = { + knownUnixlikePackages, + knownWindowsPackages, + generateBinPath, + downloadedBinPath, + pkgAndSubpathForCurrentPlatform, + TURBO_BINARY_PATH, +}; diff --git a/packages/turbo/package.json b/packages/turbo/package.json new file mode 100644 index 0000000..4627a33 --- /dev/null +++ b/packages/turbo/package.json @@ -0,0 +1,29 @@ +{ + "name": "turbo", + "version": "1.9.4-canary.2", + "description": "Turborepo is a high-performance build system for JavaScript and TypeScript codebases.", + "repository": "https://github.com/vercel/turbo", + "bugs": "https://github.com/vercel/turbo/issues", + "homepage": "https://turbo.build/repo", + "license": "MPL-2.0", + "scripts": { + "postversion": "node bump-version.js", + "postinstall": "node install.js" + }, + "bin": { + "turbo": "./bin/turbo" + }, + "files": [ + "bin", + "node-platform.js", + "install.js" + ], + "optionalDependencies": { + "turbo-darwin-64": "1.9.4-canary.2", + "turbo-darwin-arm64": "1.9.4-canary.2", + "turbo-linux-64": "1.9.4-canary.2", + "turbo-linux-arm64": "1.9.4-canary.2", + "turbo-windows-64": "1.9.4-canary.2", + "turbo-windows-arm64": "1.9.4-canary.2" + } +} |
