aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/packages/turbo/node-platform.js
diff options
context:
space:
mode:
author简律纯 <hsiangnianian@outlook.com>2023-04-28 01:36:44 +0800
committer简律纯 <hsiangnianian@outlook.com>2023-04-28 01:36:44 +0800
commitdd84b9d64fb98746a230cd24233ff50a562c39c9 (patch)
treeb583261ef00b3afe72ec4d6dacb31e57779a6faf /packages/turbo/node-platform.js
parent0b46fcd72ac34382387b2bcf9095233efbcc52f4 (diff)
downloadHydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.tar.gz
HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.zip
Diffstat (limited to 'packages/turbo/node-platform.js')
-rw-r--r--packages/turbo/node-platform.js257
1 files changed, 257 insertions, 0 deletions
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,
+};