From 4abce9a032821d4c70254187ff9af3906f4292c1 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 09:19:14 +0000 Subject: [PATCH] perf: optimize fs.readFileSync memory allocation Refactor instances of `fs.readFileSync(path).toString()` to `fs.readFileSync(path, 'utf8')` to avoid unnecessary intermediate Buffer allocations and reduce GC overhead. Co-authored-by: vishnu-madhavan-git <237662584+vishnu-madhavan-git@users.noreply.github.com> --- .jules/bolt.md | 3 +++ .../cli/src/start/project/__tests__/devices-test.ts | 2 +- packages/@expo/cli/src/utils/mergeGitIgnorePaths.ts | 4 ++-- packages/@expo/config-plugins/src/android/Package.ts | 4 ++-- .../android/__tests__/renamePackageOnDisk-test.ts | 12 ++++++------ .../withAndroidSplashLegacyMainActivity-test.ts | 2 +- 6 files changed, 15 insertions(+), 12 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 00000000000000..dbcafdf1880d7d --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2026-04-24 - Optimization: Direct encoding in fs.readFileSync +**Learning:** In Node.js components of the codebase, avoid unnecessary intermediate memory allocations by passing the encoding string directly to fs.readFileSync (e.g., `fs.readFileSync(file, 'utf8')`) instead of calling `.toString()` on the resulting Buffer. This serves as a valid micro-optimization. +**Action:** Replaced instances of `fs.readFileSync(...).toString()` with `fs.readFileSync(..., 'utf8')` and correctly updated type assertions (`as string`) in TypeScript tests utilizing memfs. diff --git a/packages/@expo/cli/src/start/project/__tests__/devices-test.ts b/packages/@expo/cli/src/start/project/__tests__/devices-test.ts index 47cca6966ff94d..eba16349f5c3c3 100644 --- a/packages/@expo/cli/src/start/project/__tests__/devices-test.ts +++ b/packages/@expo/cli/src/start/project/__tests__/devices-test.ts @@ -26,7 +26,7 @@ describe('devices info', () => { const file = path.join(projectRoot, '.expo', 'devices.json'); expect(fs.existsSync(file)).toBe(true); - const { devices } = JSON.parse(fs.readFileSync(file, 'utf8').toString()); + const { devices } = JSON.parse(fs.readFileSync(file, 'utf8') as string); expect(devices.length).toBe(1); expect(devices[0].installationId).toBe('test-device-id'); }); diff --git a/packages/@expo/cli/src/utils/mergeGitIgnorePaths.ts b/packages/@expo/cli/src/utils/mergeGitIgnorePaths.ts index dab487df678413..8fe635d49973c9 100644 --- a/packages/@expo/cli/src/utils/mergeGitIgnorePaths.ts +++ b/packages/@expo/cli/src/utils/mergeGitIgnorePaths.ts @@ -35,8 +35,8 @@ export function mergeGitIgnorePaths( return null; } - const targetGitIgnore = fs.readFileSync(targetGitIgnorePath).toString(); - const sourceGitIgnore = fs.readFileSync(sourceGitIgnorePath).toString(); + const targetGitIgnore = fs.readFileSync(targetGitIgnorePath, 'utf8'); + const sourceGitIgnore = fs.readFileSync(sourceGitIgnorePath, 'utf8'); const merged = mergeGitIgnoreContents(targetGitIgnore, sourceGitIgnore); // Only rewrite the file if it was modified. if (merged.contents) { diff --git a/packages/@expo/config-plugins/src/android/Package.ts b/packages/@expo/config-plugins/src/android/Package.ts index 96222ebf4ba553..052004d5ed719e 100644 --- a/packages/@expo/config-plugins/src/android/Package.ts +++ b/packages/@expo/config-plugins/src/android/Package.ts @@ -125,7 +125,7 @@ export async function renameJniOnDiskForType({ filesToUpdate.forEach((filepath: string) => { try { if (fs.lstatSync(filepath).isFile() && ['.h', '.cpp'].includes(path.extname(filepath))) { - let contents = fs.readFileSync(filepath).toString(); + let contents = fs.readFileSync(filepath, 'utf8'); contents = contents.replace( new RegExp(transformJavaClassDescriptor(currentPackageName).replace(/\//g, '\\/'), 'g'), transformJavaClassDescriptor(packageName) @@ -207,7 +207,7 @@ export async function renamePackageOnDiskForType({ filesToUpdate.forEach((filepath: string) => { try { if (fs.lstatSync(filepath).isFile()) { - let contents = fs.readFileSync(filepath).toString(); + let contents = fs.readFileSync(filepath, 'utf8'); if (path.extname(filepath) === '.kt') { contents = replacePackageName(contents, currentPackageName, kotlinSanitizedPackageName); } else { diff --git a/packages/@expo/config-plugins/src/android/__tests__/renamePackageOnDisk-test.ts b/packages/@expo/config-plugins/src/android/__tests__/renamePackageOnDisk-test.ts index af4d7c87c84aa5..13797d4ab289e1 100644 --- a/packages/@expo/config-plugins/src/android/__tests__/renamePackageOnDisk-test.ts +++ b/packages/@expo/config-plugins/src/android/__tests__/renamePackageOnDisk-test.ts @@ -44,24 +44,24 @@ public class SomeClass { await renamePackageOnDisk({ android: { package: 'xyz.bront.app' } }, '/myapp'); const mainActivityPath = '/myapp/android/app/src/main/java/xyz/bront/app/MainActivity.java'; expect(fs.existsSync(mainActivityPath)).toBeTruthy(); - expect(fs.readFileSync(mainActivityPath).toString()).toMatch('package xyz.bront.app'); + expect(fs.readFileSync(mainActivityPath, 'utf8') as string).toMatch('package xyz.bront.app'); const nestedClassPath = '/myapp/android/app/src/main/java/xyz/bront/app/example/SomeClass.java'; expect(fs.existsSync(nestedClassPath)).toBeTruthy(); - expect(fs.readFileSync(nestedClassPath).toString()).toMatch('package xyz.bront.app'); - expect(fs.readFileSync(nestedClassPath).toString()).not.toMatch('com.lololol'); + expect(fs.readFileSync(nestedClassPath, 'utf8') as string).toMatch('package xyz.bront.app'); + expect(fs.readFileSync(nestedClassPath, 'utf8') as string).not.toMatch('com.lololol'); const buckPath = '/myapp/android/app/BUCK'; - expect(fs.readFileSync(buckPath).toString()).toMatch('package = "xyz.bront.app"'); - expect(fs.readFileSync(buckPath).toString()).not.toMatch('com.lololol'); + expect(fs.readFileSync(buckPath, 'utf8') as string).toMatch('package = "xyz.bront.app"'); + expect(fs.readFileSync(buckPath, 'utf8') as string).not.toMatch('com.lololol'); }); it('does not clobber itself if package has similar parts', async () => { await renamePackageOnDisk({ android: { package: 'com.bront' } }, '/myapp'); const mainActivityPath = '/myapp/android/app/src/main/java/com/bront/MainActivity.java'; expect(fs.existsSync(mainActivityPath)).toBeTruthy(); - expect(fs.readFileSync(mainActivityPath).toString()).toMatch('package com.bront'); + expect(fs.readFileSync(mainActivityPath, 'utf8') as string).toMatch('package com.bront'); }); }); }); diff --git a/packages/@expo/prebuild-config/src/plugins/unversioned/expo-splash-screen/__tests__/withAndroidSplashLegacyMainActivity-test.ts b/packages/@expo/prebuild-config/src/plugins/unversioned/expo-splash-screen/__tests__/withAndroidSplashLegacyMainActivity-test.ts index edbcea2fa9eb34..aef0402929e9ba 100644 --- a/packages/@expo/prebuild-config/src/plugins/unversioned/expo-splash-screen/__tests__/withAndroidSplashLegacyMainActivity-test.ts +++ b/packages/@expo/prebuild-config/src/plugins/unversioned/expo-splash-screen/__tests__/withAndroidSplashLegacyMainActivity-test.ts @@ -28,7 +28,7 @@ describe(setSplashScreenLegacyMainActivity, () => { }, }; const mainActivity = await AndroidConfig.Paths.getMainActivityAsync('/app'); - let contents = fs.readFileSync(mainActivity.path).toString(); + let contents = fs.readFileSync(mainActivity.path, 'utf8') as string; contents = await setSplashScreenLegacyMainActivity( exp, { backgroundColor: '#000020', resizeMode: 'native' },