Skip to content

Commit b6cd01c

Browse files
committed
Expo: fix missing metro config/cli, scope prebuild to the platform under test
Two functional bugs in the Expo test path, both exposed by the xcodebuild-bypass change: create-expo-app's blank template ships without a metro.config.js, and Expo doesn't depend on @react-native-community/cli itself - but react-native-xcode.sh's bundling step (used by both the initial build and the fast-path scenario-switch rebuild) shells out to react-native's own cli.js, which needs both to be present. Add the missing metro.config.js customize step and the cli devDependency. Also address the resulting perf cost of always running `expo prebuild` for both platforms: - Scope `expo prebuild` to only the platform actually under test in this mocha run (`--platform ios`/`--platform android`), instead of the default "all" - this was the single largest win found in the whole investigation, since prebuild was regenerating an entire unused platform's native project on every project setup. - Drop `--clean` from the repeated `expo prebuild` call in createUpdateArchive: that project's native tree is already a clean Expo-managed one from setupProject, app.json never changes between these repeated calls, and nothing ever builds this project's native code (only `react-native bundle` reads from it) - a full wipe-and- regenerate here was pure wasted cost since incremental reconciliation is a no-op in this case. setupProject's own prebuild call keeps --clean, since it needs to wipe the bare-RN template files that copyTemplate copies in ahead of it.
1 parent 82b7f0b commit b6cd01c

1 file changed

Lines changed: 33 additions & 9 deletions

File tree

test/test.ts

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ function ensureAndroidCleartextTraffic(androidManifestPath: string): void {
3434
}
3535
}
3636

37+
/**
38+
* Returns a " --platform <ios|android>" flag for `expo prebuild` when exactly one platform is
39+
* under test in this mocha run, so prebuild only regenerates that platform's native project
40+
* instead of defaulting to "all". Falls back to no flag (i.e. "all") if both/neither are active.
41+
*/
42+
function getExpoPrebuildPlatformFlag(): string {
43+
const ios = TestUtil.readMochaCommandLineFlag("--ios");
44+
const android = TestUtil.readMochaCommandLineFlag("--android");
45+
if (ios && !android) return " --platform ios";
46+
if (android && !ios) return " --platform android";
47+
return "";
48+
}
49+
3750
function installExpoBundleTooling(projectPath: string): Q.Promise<void> {
3851
const packageJsonPath = path.join(projectPath, "package.json");
3952
const packageJsonContents = fs.readFileSync(packageJsonPath, "utf8");
@@ -44,8 +57,11 @@ function installExpoBundleTooling(projectPath: string): Q.Promise<void> {
4457
throw new Error(`Could not determine react-native version from ${packageJsonPath}`);
4558
}
4659

60+
// Expo doesn't depend on @react-native-community/cli itself, but react-native-xcode.sh's
61+
// bundling step shells out to react-native's own cli.js, which requires it to be present
62+
// as a devDependency (used later both for the fast-path iOS rebuild and for update bundling).
4763
return TestUtil.getProcessOutput(
48-
`npm install --save-dev @react-native/metro-config@${reactNativeVersion}`,
64+
`npm install --save-dev @react-native/metro-config@${reactNativeVersion} @react-native-community/cli`,
4965
{ cwd: projectPath, noLogStdOut: true }
5066
).then(() => { return null; });
5167
}
@@ -421,9 +437,17 @@ class RNProjectManager extends ProjectManager {
421437
.then<void>(TestUtil.getProcessOutput.bind(undefined, TestConfig.thisPluginInstallString, { cwd: path.join(projectDirectory, TestConfig.TestAppName), noLogStdOut: true, noLogStdErr: true }))
422438
.then(installExpoBundleTooling.bind(undefined, path.join(projectDirectory, TestConfig.TestAppName)))
423439
.then<void>(TestUtil.getProcessOutput.bind(undefined, "npx expo install expo-build-properties", { cwd: path.join(projectDirectory, TestConfig.TestAppName), noLogStdOut: true }))
424-
.then(TestUtil.getProcessOutput.bind(undefined, `npx expo prebuild --clean`, { cwd: path.join(projectDirectory, TestConfig.TestAppName), noLogStdOut: true }))
440+
// create-expo-app's blank template ships without a metro.config.js. react-native-xcode.sh's
441+
// bundling step (used both for the initial build and for fast-path scenario-switch rebuilds)
442+
// shells out to react-native's cli.js, which throws "No Metro config found" without one.
443+
.then<void>(TestUtil.getProcessOutput.bind(undefined, "npx expo customize metro.config.js", { cwd: path.join(projectDirectory, TestConfig.TestAppName), noLogStdOut: true }))
444+
.then(TestUtil.getProcessOutput.bind(undefined, `npx expo prebuild --clean${getExpoPrebuildPlatformFlag()}`, { cwd: path.join(projectDirectory, TestConfig.TestAppName), noLogStdOut: true }))
425445
.then(() => {
426-
ensureAndroidCleartextTraffic(path.join(projectDirectory, TestConfig.TestAppName, "android", "app", "src", "main", "AndroidManifest.xml"));
446+
// Skipped entirely on iOS-only runs, where prebuild no longer generates the android/ folder.
447+
const androidManifestPath = path.join(projectDirectory, TestConfig.TestAppName, "android", "app", "src", "main", "AndroidManifest.xml");
448+
if (fs.existsSync(androidManifestPath)) {
449+
ensureAndroidCleartextTraffic(androidManifestPath);
450+
}
427451
return null;
428452
})
429453
.then(() => { return null; });
@@ -503,13 +527,13 @@ class RNProjectManager extends ProjectManager {
503527
if (TestConfig.isExpoApp) {
504528
// Using react-native bundle instead of expo export because code-push-cli uses react-native-cli to build the app.
505529
return deferred.promise
506-
.then(TestUtil.getProcessOutput.bind(undefined, "npx expo prebuild --clean", { cwd: path.join(projectDirectory, TestConfig.TestAppName) }))
507-
.then(TestUtil.getProcessOutput.bind(undefined, "npx expo customize metro.config.js",
508-
{ cwd: path.join(projectDirectory, TestConfig.TestAppName) }))
509-
.then(TestUtil.getProcessOutput.bind(undefined, "npm install @react-native-community/cli",
510-
{ cwd: path.join(projectDirectory, TestConfig.TestAppName) }))
530+
// No `prebuild --clean`: this project's native tree is already a clean Expo-managed one from
531+
// setupProject, app.json never changes between these repeated calls, and nothing ever
532+
// builds this project's native code (only `react-native bundle` reads from it) - a full
533+
// wipe-and-regenerate here is pure wasted cost, incremental reconciliation is a no-op.
534+
.then(TestUtil.getProcessOutput.bind(undefined, "npx expo prebuild --platform " + targetPlatform.getName(), { cwd: path.join(projectDirectory, TestConfig.TestAppName), noLogStdOut: true }))
511535
.then(TestUtil.getProcessOutput.bind(undefined, "npx react-native bundle --entry-file index.js --platform " + targetPlatform.getName() + " --bundle-output " + bundlePath + " --assets-dest " + bundleFolder + " --dev false",
512-
{ cwd: path.join(projectDirectory, TestConfig.TestAppName) }))
536+
{ cwd: path.join(projectDirectory, TestConfig.TestAppName), noLogStdOut: true }))
513537
.then<string>(TestUtil.archiveFolder.bind(undefined, bundleFolder, "", path.join(projectDirectory, TestConfig.TestAppName, "update.zip"), isDiff))
514538
.then((result) => { console.log(`[TIMING] createUpdateArchive(${projectDirectory}, ${targetPlatform.getName()}) took ${Date.now() - t0}ms`); return result; });
515539
} else {

0 commit comments

Comments
 (0)