Files
paseo/scripts/postinstall-patches.mjs
T
5d70bf0645 fix(android): restore image picker after Activity recreation (#7546)
Backport expo/expo#49634 to SDK 54 so retained launchers register
against the current Activity after their previous owner is destroyed.
Cover repeated recreation and replacement-before-destroy ordering with
native regression tests, and document the native rebuild requirement.

Co-authored-by: luke <luopeihuan@gmail.com>
Co-authored-by: Ido Yana <idoyana@gmail.com>
Co-authored-by: RoundRedDot <roundreddot@gmail.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-10-21 09:17:26 +02:00

124 lines
4.5 KiB
JavaScript

import { copyFileSync, existsSync, mkdirSync, readdirSync, rmSync } from "node:fs";
import { spawnSync } from "node:child_process";
import { join, relative } from "node:path";
// In CI we often install a single workspace (e.g. server/relay/website). Only apply patches
// when the patched dependency is actually present.
// `cwd` is where patch-package must run from. Packages that npm does not hoist to the
// workspace root live in their workspace's own node_modules, and patch-package resolves
// the patch's node_modules/... paths relative to its working directory.
const patchedPackages = [
// Backport for expo-modules-core 57.0.23: https://github.com/expo/expo/pull/49634.
// Remove when the installed Expo SDK includes the Activity recreation fix.
{
nodeModulesPath: "node_modules/expo-modules-core",
patchPrefix: "expo-modules-core+",
},
{
nodeModulesPath: "packages/app/node_modules/react-native-enriched-markdown",
patchPrefix: "react-native-enriched-markdown+",
cwd: "packages/app",
},
// Remove after react-native-unistyles ships
// https://github.com/jpudysz/react-native-unistyles/pull/1203.
{
nodeModulesPath: "node_modules/react-native-unistyles",
patchPrefix: "react-native-unistyles+",
},
{
nodeModulesPath: "node_modules/react-native-draggable-flatlist",
patchPrefix: "react-native-draggable-flatlist+",
},
{
nodeModulesPath: "node_modules/react-native-gesture-handler",
patchPrefix: "react-native-gesture-handler+",
},
// Exports BottomSheetModalInternalProvider so a subtree can host its own sheets on the shared
// modal stack (see packages/app/src/components/ui/isolated-bottom-sheet-modal/host-scope.tsx), and
// keeps a dismissing modal's status through its final onChange(-1), so a modal with
// enableDismissOnClose=false that unmounts while presented still leaves the portal.
{
nodeModulesPath: "node_modules/@gorhom/bottom-sheet",
patchPrefix: "@gorhom+bottom-sheet+",
},
{
nodeModulesPath: "packages/app/node_modules/react-native-svg",
patchPrefix: "react-native-svg+",
cwd: "packages/app",
},
{
nodeModulesPath: "node_modules/@mattermost/react-native-paste-input",
patchPrefix: "@mattermost+react-native-paste-input+",
},
{
nodeModulesPath: "packages/server/node_modules/@opencode-ai/sdk",
patchPrefix: "@opencode-ai+sdk+",
cwd: "packages/server",
},
];
const installedPackages = patchedPackages.filter(({ nodeModulesPath }) =>
existsSync(nodeModulesPath),
);
if (!existsSync("patches") || installedPackages.length === 0) {
process.exit(0);
}
const patchFiles = readdirSync("patches").filter((file) => file.endsWith(".patch"));
const isWindows = process.platform === "win32";
const cmd = isWindows ? "patch-package.cmd" : "patch-package";
let failed = false;
for (const [index, { nodeModulesPath, patchPrefix, cwd = "." }] of installedPackages.entries()) {
const files = patchFiles.filter((file) => file.startsWith(patchPrefix));
if (files.length === 0) {
continue;
}
const outcome = applyPatches(cwd, files, index);
if (outcome === "applied") {
continue;
}
failed = true;
if (outcome === "unavailable") {
continue;
}
// patch-package cannot apply a patch that changed after an earlier version of it was
// applied. Without the package, the next install fetches it clean and applies the patch.
rmSync(nodeModulesPath, { recursive: true, force: true });
console.error(
`postinstall-patches: removed ${nodeModulesPath} because its patch did not apply. ` +
"Run npm install again to reinstall it with the current patch. " +
"If it fails again on the clean package, the patch is out of date and must be regenerated.",
);
}
process.exit(failed ? 1 : 0);
function applyPatches(cwd, files, index) {
const tempPatchDir = join(".tmp", `postinstall-patches-${process.pid}-${index}`);
mkdirSync(tempPatchDir, { recursive: true });
for (const patchFile of files) {
copyFileSync(join("patches", patchFile), join(tempPatchDir, patchFile));
}
let result;
try {
result = spawnSync(cmd, ["--patch-dir", relative(cwd, tempPatchDir), "--error-on-fail"], {
cwd,
shell: isWindows,
stdio: "inherit",
windowsHide: true,
});
} finally {
rmSync(tempPatchDir, { recursive: true, force: true });
}
if (result.error) {
console.error("postinstall-patches: patch-package failed to spawn:", result.error.message);
return "unavailable";
}
return result.status === 0 ? "applied" : "rejected";
}