Skip to content

Commit f509d4a

Browse files
committed
Add script to init a MacOS test app
Enable Hermes and Fabric Patch react_native_post_install to pass react native path Don't pod install when initializing Patch macos app with scripts and source files Re-arm the init script Move linked deps into install command to make --install-links effective Using regular linking for monorepo deps Include original dependencies Enable new arch in Podfile Fix comment from review
1 parent f83c5c5 commit f509d4a

File tree

6 files changed

+221
-14
lines changed

6 files changed

+221
-14
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ node_modules/
44
dist/
55

66
*.tsbuildinfo
7+
8+
# Treading the MacOS app as ephemeral
9+
apps/macos-test-app

package-lock.json

Lines changed: 14 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
"test": "npm test --workspace react-native-node-api --workspace cmake-rn --workspace gyp-to-cmake --workspace node-addon-examples",
2727
"bootstrap": "node --run build && npm run bootstrap --workspaces --if-present",
2828
"prerelease": "node --run build && npm run prerelease --workspaces --if-present",
29-
"release": "changeset publish"
29+
"release": "changeset publish",
30+
"init-macos-test-app": "node scripts/init-macos-test-app.ts"
3031
},
3132
"author": {
3233
"name": "Callstack",
@@ -64,6 +65,7 @@
6465
"globals": "^16.0.0",
6566
"prettier": "^3.6.2",
6667
"react-native": "0.81.4",
68+
"read-pkg": "^9.0.1",
6769
"tsx": "^4.20.5",
6870
"typescript": "^5.8.0",
6971
"typescript-eslint": "^8.38.0"

scripts/init-macos-test-app.ts

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
import assert from "node:assert/strict";
2+
import cp from "node:child_process";
3+
import fs from "node:fs";
4+
import path from "node:path";
5+
import { readPackage } from "read-pkg";
6+
7+
const REACT_NATIVE_VERSION = "0.79.6";
8+
const ROOT_PATH = path.join(import.meta.dirname, "..");
9+
const APP_PATH = path.join(ROOT_PATH, "apps", "macos-test-app");
10+
const OTHER_APP_PATH = path.join(ROOT_PATH, "apps", "test-app");
11+
12+
function exec(command: string, args: string[], options: cp.SpawnOptions = {}) {
13+
const { status } = cp.spawnSync(command, args, {
14+
stdio: "inherit",
15+
...options,
16+
});
17+
assert.equal(status, 0, `Failed to execute '${command}'`);
18+
}
19+
20+
async function deletePreviousApp() {
21+
if (fs.existsSync(APP_PATH)) {
22+
console.log("Deleting existing app directory");
23+
await fs.promises.rm(APP_PATH, { recursive: true, force: true });
24+
}
25+
}
26+
27+
async function initializeReactNativeTemplate() {
28+
console.log("Initializing community template");
29+
exec("npx", [
30+
"@react-native-community/cli",
31+
"init",
32+
"MacOSTestApp",
33+
"--skip-install",
34+
"--skip-git-init",
35+
// "--platform-name",
36+
// "react-native-macos",
37+
"--version",
38+
REACT_NATIVE_VERSION,
39+
"--directory",
40+
APP_PATH,
41+
]);
42+
43+
// Clean up
44+
const CLEANUP_PATHS = [
45+
"ios",
46+
"android",
47+
"__tests__",
48+
".prettierrc.js",
49+
".gitignore",
50+
];
51+
52+
for (const cleanupPath of CLEANUP_PATHS) {
53+
await fs.promises.rm(path.join(APP_PATH, cleanupPath), {
54+
recursive: true,
55+
force: true,
56+
});
57+
}
58+
}
59+
60+
async function patchPackageJson() {
61+
console.log("Patching package.json scripts");
62+
const packageJson = await readPackage({ cwd: APP_PATH });
63+
const otherPackageJson = await readPackage({ cwd: OTHER_APP_PATH });
64+
65+
packageJson.scripts = {
66+
...packageJson.scripts,
67+
metro: "react-native start --reset-cache --no-interactive",
68+
"mocha-and-metro": "mocha-remote --exit-on-error -- node --run metro",
69+
premacos: "killall 'MacOSTestApp' || true",
70+
macos: "react-native run-macos --no-packager",
71+
test: "mocha-remote --exit-on-error -- concurrently --passthrough-arguments --kill-others-on-fail npm:metro 'npm:macos -- {@}' --",
72+
"test:allTests": "MOCHA_REMOTE_CONTEXT=allTests node --run test -- ",
73+
"test:nodeAddonExamples":
74+
"MOCHA_REMOTE_CONTEXT=nodeAddonExamples node --run test -- ",
75+
"test:nodeTests": "MOCHA_REMOTE_CONTEXT=nodeTests node --run test -- ",
76+
"test:ferricExample":
77+
"MOCHA_REMOTE_CONTEXT=ferricExample node --run test -- ",
78+
};
79+
80+
const transferredDependencies = new Set([
81+
"@rnx-kit/metro-config",
82+
"mocha-remote-cli",
83+
"mocha-remote-react-native",
84+
]);
85+
86+
const { dependencies: otherDependencies = {} } = otherPackageJson;
87+
88+
packageJson.dependencies = {
89+
...packageJson.dependencies,
90+
"react-native-macos-init": "^2.1.3",
91+
"@react-native-node-api/node-addon-examples": path.relative(
92+
APP_PATH,
93+
path.join(ROOT_PATH, "packages", "node-addon-examples"),
94+
),
95+
"@react-native-node-api/node-tests": path.relative(
96+
APP_PATH,
97+
path.join(ROOT_PATH, "packages", "node-tests"),
98+
),
99+
"@react-native-node-api/ferric-example": path.relative(
100+
APP_PATH,
101+
path.join(ROOT_PATH, "packages", "ferric-example"),
102+
),
103+
"react-native-node-api": path.relative(
104+
APP_PATH,
105+
path.join(ROOT_PATH, "packages", "host"),
106+
),
107+
...Object.fromEntries(
108+
Object.entries(otherDependencies).filter(([name]) =>
109+
transferredDependencies.has(name),
110+
),
111+
),
112+
};
113+
114+
await fs.promises.writeFile(
115+
path.join(APP_PATH, "package.json"),
116+
JSON.stringify(packageJson, null, 2),
117+
"utf8",
118+
);
119+
}
120+
121+
function installDependencies() {
122+
console.log("Installing dependencies");
123+
exec("npm", ["install", "--prefer-offline"], {
124+
cwd: APP_PATH,
125+
});
126+
}
127+
128+
function initializeReactNativeMacOSTemplate() {
129+
console.log("Initializing react-native-macos template");
130+
exec("npx", ["react-native-macos-init"], {
131+
cwd: APP_PATH,
132+
});
133+
}
134+
135+
async function patchPodfile() {
136+
console.log("Patching Podfile");
137+
const replacements = [
138+
[
139+
// As per https://github.com/microsoft/react-native-macos/issues/2723#issuecomment-3392930688
140+
"require_relative '../node_modules/react-native-macos/scripts/react_native_pods'\nrequire_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'",
141+
"require_relative '../node_modules/react-native-macos/scripts/cocoapods/autolinking'",
142+
],
143+
[
144+
":hermes_enabled => false,",
145+
// Adding the new_arch_enabled here as it's not a part of the template
146+
":hermes_enabled => true,\n :new_arch_enabled => true,",
147+
],
148+
[
149+
":fabric_enabled => ENV['RCT_NEW_ARCH_ENABLED'] == '1',",
150+
":fabric_enabled => true,",
151+
],
152+
[
153+
"react_native_post_install(installer)",
154+
"react_native_post_install(installer, '../node_modules/react-native-macos')",
155+
],
156+
];
157+
158+
const podfilePath = path.join(APP_PATH, "macos", "Podfile");
159+
let podfileContents = await fs.promises.readFile(podfilePath, "utf8");
160+
for (const [searchValue, replaceValue] of replacements) {
161+
podfileContents = podfileContents.replace(searchValue, replaceValue);
162+
}
163+
await fs.promises.writeFile(podfilePath, podfileContents, "utf8");
164+
}
165+
166+
async function copySourceFiles() {
167+
console.log("Copying source files from test-app into macos-test-app:");
168+
const FILE_NAMES = [
169+
"App.tsx",
170+
// Adds the babel plugin needed to transform require calls
171+
"babel.config.js",
172+
// Adds the ability to reference symlinked packages
173+
"metro.config.js",
174+
];
175+
for (const fileName of FILE_NAMES) {
176+
console.log(`↳ ${fileName}`);
177+
await fs.promises.copyFile(
178+
path.join(OTHER_APP_PATH, fileName),
179+
path.join(APP_PATH, fileName),
180+
);
181+
}
182+
}
183+
184+
await deletePreviousApp();
185+
await initializeReactNativeTemplate();
186+
await patchPackageJson();
187+
installDependencies();
188+
initializeReactNativeMacOSTemplate();
189+
await patchPodfile();
190+
await copySourceFiles();

tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
},
77
"files": ["prettier.config.js", "eslint.config.js"],
88
"references": [
9+
{ "path": "./tsconfig.scripts.json" },
910
{ "path": "./packages/cli-utils/tsconfig.json" },
1011
{ "path": "./packages/cmake-file-api/tsconfig.json" },
1112
{ "path": "./packages/cmake-file-api/tsconfig.tests.json" },

tsconfig.scripts.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"extends": "./configs/tsconfig.node.json",
3+
"compilerOptions": {
4+
"composite": true,
5+
"emitDeclarationOnly": true,
6+
"declarationMap": false,
7+
"rootDir": "scripts"
8+
},
9+
"include": ["scripts"]
10+
}

0 commit comments

Comments
 (0)