Skip to content

fix: ios example duplicated symbols due to codegen #757

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import kleur from 'kleur';
import type { Input } from '../types';
import { patchCodegenAndroidPackage } from '../utils/patchCodegenAndroidPackage';
import type { Input } from '../../types';
import { patchCodegenAndroidPackage } from './patches/patchCodegenAndroidPackage';
import fs from 'fs-extra';
import path from 'path';
import del from 'del';
import { runRNCCli } from '../utils/runRNCCli';
import { runRNCCli } from '../../utils/runRNCCli';
import { removeCodegenAppLevelCode } from './patches/removeCodegenAppLevelCode';

type Options = Input;

Expand Down Expand Up @@ -47,6 +48,7 @@ export default async function build({ root, report }: Options) {
if (codegenType === 'modules' || codegenType === 'all') {
await patchCodegenAndroidPackage(root, packageJson, report);
}
await removeCodegenAppLevelCode(root, packageJson);

report.success('Generated native code with codegen');
} catch (e: unknown) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { expect, it, describe, beforeEach, afterEach } from '@jest/globals';
import fs from 'fs-extra';
import path from 'node:path';
import { patchCodegenAndroidPackage } from '../utils/patchCodegenAndroidPackage';
import { patchCodegenAndroidPackage } from './patchCodegenAndroidPackage';
import mockfs from 'mock-fs';
import type { Report } from '../types';
import type { Report } from '../../../types';

const mockPackageJson = {
codegenConfig: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import fs from 'fs-extra';
import path from 'path';
import type { Report } from '../types';
import type { Report } from '../../../types';

const CODEGEN_DOCS =
'https://github.com/reactwg/react-native-new-architecture/blob/main/docs/enable-libraries-prerequisites.md#configure-codegen';
export const CODEGEN_DOCS =
'https://reactnative.dev/docs/the-new-architecture/using-codegen#configuring-codegen';

/**
* Currently, running react-native codegen generates java files with package name `com.facebook.fbreact.specs`.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { expect, it, describe, beforeEach, afterEach } from '@jest/globals';
import fs from 'fs-extra';
import path from 'node:path';
import { removeCodegenAppLevelCode } from './removeCodegenAppLevelCode';
import mockfs from 'mock-fs';

const mockPackageJson = {
codegenConfig: {
outputDir: {
android: 'android/generated',
ios: 'ios/generated',
},
},
};

const mockProjectPath = path.resolve(__dirname, 'mockProject');

describe('patchCodegenAndroidPackage', () => {
beforeEach(() => {
mockfs({
[mockProjectPath]: {
'package.json': JSON.stringify(mockPackageJson),
'ios': {
generated: {
'RCTAppDependencyProvider.h': '',
'RCTAppDependencyProvider.mm': '',
'RCTModulesConformingToProtocolsProvider.h': '',
'RCTModulesConformingToProtocolsProvider.mm': '',
'RCTThirdPartyComponentsProvider.h': '',
'RCTThirdPartyComponentsProvider.mm': '',
'ReactAppDependencyProvider.podspec': '',
},
},
'android': {
generated: {
'RCTAppDependencyProvider.h': '',
'RCTAppDependencyProvider.mm': '',
'RCTModulesConformingToProtocolsProvider.h': '',
'RCTModulesConformingToProtocolsProvider.mm': '',
'RCTThirdPartyComponentsProvider.h': '',
'RCTThirdPartyComponentsProvider.mm': '',
'ReactAppDependencyProvider.podspec': '',
},
},
},
});
});

afterEach(() => {
mockfs.restore();
});

it('removes the duplicate iOS files', async () => {
await removeCodegenAppLevelCode(mockProjectPath, mockPackageJson);

expect(
(
await fs.promises.readdir(
path.join(mockProjectPath, 'ios', 'generated')
)
).length
).toBe(0);
});

it('removes the unnecessary Android files', async () => {
await removeCodegenAppLevelCode(mockProjectPath, mockPackageJson);

expect(
(
await fs.promises.readdir(
path.join(mockProjectPath, 'android', 'generated')
)
).length
).toBe(0);
});

it("doesn't crash the process when there are no files to remove", async () => {
mockfs({
[mockProjectPath]: {
'package.json': JSON.stringify(mockPackageJson),
'ios': {
generated: {
someRandomFile: '',
},
},
'android': {
generated: {
someRandomFile: '',
},
},
},
});

await expect(
removeCodegenAppLevelCode(mockProjectPath, mockPackageJson)
).resolves.not.toThrow();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import fs from 'fs-extra';
import path from 'path';
import { CODEGEN_DOCS } from './patchCodegenAndroidPackage';

const FILES_TO_REMOVE = [
'RCTAppDependencyProvider.h',
'RCTAppDependencyProvider.mm',
'RCTModulesConformingToProtocolsProvider.h',
'RCTModulesConformingToProtocolsProvider.mm',
'RCTThirdPartyComponentsProvider.h',
'RCTThirdPartyComponentsProvider.mm',
'ReactAppDependencyProvider.podspec',
];

/**
* With React Native 0.77, calling `@react-native-community/cli codegen` generates
* some app level source files such as `RCTAppDependencyProvider.mm`.
* These files are supposed to be only generated for apps
* but the cli misbehaves and generates them for all sorts of projects.
* You can find the relevant PR here: https://github.com/facebook/react-native/pull/47650
* This patch can be removed when this gets fixed in React Native.
*/
export async function removeCodegenAppLevelCode(
projectPath: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
packageJson: any
) {
const codegenAndroidPathSetting: string | undefined =
packageJson.codegenConfig?.outputDir?.android;
if (!codegenAndroidPathSetting) {
throw new Error(
`Your package.json doesn't contain codegenConfig.outputDir.android. Please see ${CODEGEN_DOCS}`
);
}
const codegenAndroidPath = path.resolve(
projectPath,
codegenAndroidPathSetting
);

if (!(await fs.pathExists(codegenAndroidPath))) {
throw new Error(
`The codegen android path defined in your package.json: ${codegenAndroidPath} doesnt' exist.`
);
}

const codegenIosPathSetting: string | undefined =
packageJson.codegenConfig?.outputDir?.ios;
if (!codegenIosPathSetting) {
throw new Error(
`Your package.json doesn't contain codegenConfig.outputDir.ios. Please see ${CODEGEN_DOCS}`
);
}
const codegenIosPath = path.resolve(projectPath, codegenIosPathSetting);

if (!(await fs.pathExists(codegenAndroidPath))) {
throw new Error(
`The codegen iOS path defined in your package.json: ${codegenIosPathSetting} doesnt' exist.`
);
}

const androidPromises = FILES_TO_REMOVE.map((fileName) =>
fs.rm(path.join(codegenAndroidPath, fileName))
);

const iosPromises = FILES_TO_REMOVE.map((fileName) =>
fs.rm(path.join(codegenIosPath, fileName))
);

await Promise.allSettled([...androidPromises, ...iosPromises]);
}
Loading