|
| 1 | +// @ts-check |
| 2 | +/** |
| 3 | + * @typedef {{ values: Map<string, unknown>; }} Configuration |
| 4 | + * @typedef {{ cwd: string; }} Workspace |
| 5 | + * @typedef {{ configuration: Configuration; cwd: string; workspaces: Workspace[]; }} Project |
| 6 | + * @typedef {{ mode?: "skip-build" | "update-lockfile"; }} InstallOptions |
| 7 | + */ |
| 8 | + |
| 9 | +/** |
| 10 | + * Yarn always sorts `package.json` during install. This is problematic because |
| 11 | + * we need the `bin` field to be in a specific order to workaround an issue with |
| 12 | + * `bunx` always picking the first binary. |
| 13 | + * |
| 14 | + * For more context, see: |
| 15 | + * |
| 16 | + * - https://github.com/microsoft/react-native-test-app/issues/2417 |
| 17 | + * - https://github.com/yarnpkg/berry/issues/6184 |
| 18 | + * |
| 19 | + * @type {{ name: string; factory: (require: NodeJS.Require) => unknown; }} |
| 20 | + */ |
| 21 | +module.exports = { |
| 22 | + name: "plugin-undo-bin-sorting", |
| 23 | + factory: (require) => { |
| 24 | + const { npath } = require("@yarnpkg/fslib"); |
| 25 | + const fs = require("node:fs"); |
| 26 | + const path = require("node:path"); |
| 27 | + |
| 28 | + const asText = /** @type {const} */ ({ encoding: "utf-8" }); |
| 29 | + |
| 30 | + let manifestPath = ""; |
| 31 | + let orig_rawManifest = ""; |
| 32 | + return { |
| 33 | + hooks: { |
| 34 | + /** @type {(project: Project) => void} */ |
| 35 | + validateProject(project) { |
| 36 | + const projectRoot = npath.fromPortablePath(project.cwd); |
| 37 | + manifestPath = path.join(projectRoot, "package.json"); |
| 38 | + orig_rawManifest = fs.readFileSync(manifestPath, asText); |
| 39 | + }, |
| 40 | + /** @type {(project: Project, options: InstallOptions) => void} */ |
| 41 | + afterAllInstalled() { |
| 42 | + const rawManifest = fs.readFileSync(manifestPath, asText); |
| 43 | + if (rawManifest === orig_rawManifest) { |
| 44 | + return; |
| 45 | + } |
| 46 | + |
| 47 | + const manifest = JSON.parse(rawManifest); |
| 48 | + const bin = Object.keys(manifest.bin); |
| 49 | + |
| 50 | + const orig_manifest = JSON.parse(orig_rawManifest); |
| 51 | + const orig_bin = Object.keys(orig_manifest.bin); |
| 52 | + |
| 53 | + const length = bin.length; |
| 54 | + if (length !== orig_bin.length) { |
| 55 | + throw new Error("Did Yarn add something to the 'bin' field?"); |
| 56 | + } |
| 57 | + |
| 58 | + for (let i = 0; i < length; ++i) { |
| 59 | + if (bin[i] !== orig_bin[i]) { |
| 60 | + manifest.bin = orig_manifest.bin; |
| 61 | + const fd = fs.openSync(manifestPath, "w", 0o644); |
| 62 | + fs.writeSync(fd, JSON.stringify(manifest, undefined, 2)); |
| 63 | + fs.writeSync(fd, "\n"); |
| 64 | + fs.closeSync(fd); |
| 65 | + break; |
| 66 | + } |
| 67 | + } |
| 68 | + }, |
| 69 | + }, |
| 70 | + }; |
| 71 | + }, |
| 72 | +}; |
0 commit comments