* Copyright (c) 2025 Huawei Technologies Co., Ltd.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import fs from "node:fs";
import pathUtils from "node:path";
import { execSync } from "child_process";
import { unlinkSync } from "fs";
const REPO_ROOT_PATH = pathUtils.resolve(__dirname, "..");
const RNOH_PKG_ROOT_PATH = pathUtils.join(
REPO_ROOT_PATH,
"packages",
"react-native-harmony",
);
const REACT_NATIVE_SUBMODULE_PATH = pathUtils.join(
REPO_ROOT_PATH,
"packages",
"react-native",
);
const REACT_NATIVE_PATCH_PATH = pathUtils.join(
REPO_ROOT_PATH,
"packages",
"react-native.patch",
);
const BOOST_PATCH_PATH = pathUtils.join(
REPO_ROOT_PATH,
"packages",
"boost.patch",
);
const BOOST_SUBMODULE_PATH = pathUtils.join(
REPO_ROOT_PATH,
"packages",
"tester",
"harmony",
"react_native_openharmony",
"src",
"main",
"cpp",
"third-party",
"boost",
);
const REACT_NATIVE_PKG_ROOT_PATH = pathUtils.join(
REACT_NATIVE_SUBMODULE_PATH,
"packages",
"react-native",
);
const REACT_COMMON_SRC_PATH = pathUtils.join(
REACT_NATIVE_PKG_ROOT_PATH,
"ReactCommon",
);
const REACT_COMMON_DEST_PATH = pathUtils.join(
REPO_ROOT_PATH,
"packages",
"tester",
"harmony",
"react_native_openharmony",
"src",
"main",
"cpp",
"third-party",
"rn",
"ReactCommon",
);
try {
applyPatch();
applyBoostPatch();
syncJS("delegates");
syncJS("Libraries");
syncJS("src");
syncJS("types_DEPRECATED", "types");
syncCpp();
} finally {
cleanReactNativeSubmodule();
}
function applyPatch() {
cleanReactNativeSubmodule();
execSync(`git apply --whitespace=fix ${REACT_NATIVE_PATCH_PATH}`, {
cwd: REACT_NATIVE_SUBMODULE_PATH,
});
}
* Apply packages/boost.patch to the boost submodule in-place.
* Unlike react-native (flat submodule → reset --hard then apply), boost nests
* libs/* as submodules, so a parent reset does not restore patched files.
* Reverse-apply first (ignore if not previously applied), then apply — same
* idempotency as RN clean+apply, without hardcoding paths from the patch.
*/
function applyBoostPatch() {
if (!fs.existsSync(BOOST_PATCH_PATH)) {
throw new Error(`missing boost patch: ${BOOST_PATCH_PATH}`);
}
if (!fs.existsSync(BOOST_SUBMODULE_PATH)) {
throw new Error(`boost submodule not found: ${BOOST_SUBMODULE_PATH}`);
}
try {
execSync(`git apply -R --whitespace=fix ${BOOST_PATCH_PATH}`, {
cwd: BOOST_SUBMODULE_PATH,
stdio: "pipe",
});
} catch (error) {
const detail =
error instanceof Error && "stderr" in error
? String(
(error as { stderr?: Buffer | string }).stderr || error.message,
)
: String(error);
console.log(
`boost.patch reverse-apply skipped (likely not previously applied): ${detail.trim()}`,
);
}
execSync(`git apply --whitespace=fix ${BOOST_PATCH_PATH}`, {
cwd: BOOST_SUBMODULE_PATH,
});
console.log(
`applied ${pathUtils.relative(REPO_ROOT_PATH, BOOST_PATCH_PATH)} to ${pathUtils.relative(REPO_ROOT_PATH, BOOST_SUBMODULE_PATH)}`,
);
}
function cleanReactNativeSubmodule() {
execSync("git reset --hard HEAD && git clean -dfx", {
cwd: REACT_NATIVE_SUBMODULE_PATH,
});
}
function syncJS(
pathRelativeToRNRoot: string,
destPathRelativeToRNOHRoot?: string,
) {
const destPath = pathUtils.join(
RNOH_PKG_ROOT_PATH,
destPathRelativeToRNOHRoot ?? pathRelativeToRNRoot,
);
if (!fs.existsSync(destPath)) {
fs.mkdirSync(destPath);
}
removeUntrackedFiles(destPath);
fs.cpSync(
pathUtils.join(REACT_NATIVE_PKG_ROOT_PATH, pathRelativeToRNRoot),
destPath,
{ recursive: true },
);
console.log(`synchronized: ${pathUtils.relative(process.cwd(), destPath)}`);
}
function removeUntrackedFiles(dirPath: string): void {
const stdout = execSync("git ls-files --others", {
cwd: dirPath,
encoding: "utf-8",
});
const files = stdout.split("\n").filter((file) => file);
files.forEach((file) => {
const fullPath = pathUtils.resolve(dirPath, file);
unlinkSync(fullPath);
});
}
function syncCpp() {
if (!fs.existsSync(REACT_COMMON_DEST_PATH)) {
fs.mkdirSync(REACT_COMMON_DEST_PATH);
}
removeUntrackedFiles(REACT_COMMON_DEST_PATH);
fs.cpSync(REACT_COMMON_SRC_PATH, REACT_COMMON_DEST_PATH, { recursive: true });
console.log(
`synchronized: ${pathUtils.relative(process.cwd(), REACT_COMMON_DEST_PATH)}`,
);
}