From e3eb32a44455e9599798bedb2caec25a0a8f58bc Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Fri, 11 Apr 2025 21:53:46 +0200 Subject: [PATCH] fix: don't ship generated code with the library --- docs/pages/build.md | 131 +++++++++++++++--- docs/pages/faq.md | 40 ------ .../src/exampleApp/addCodegenBuildScript.ts | 58 -------- .../src/exampleApp/dependencies.ts | 14 +- .../create-react-native-library/src/index.ts | 15 +- .../templates/common-local/$package.json | 2 +- .../templates/common/$package.json | 12 +- .../native-library-new/react-native.config.js | 16 --- .../native-view-new/react-native.config.js | 16 --- .../objc-library/ios/{%- project.name %}.h | 2 +- .../ios/{%- project.name %}View.mm | 8 +- 11 files changed, 122 insertions(+), 192 deletions(-) delete mode 100644 packages/create-react-native-library/src/exampleApp/addCodegenBuildScript.ts delete mode 100644 packages/create-react-native-library/templates/native-library-new/react-native.config.js delete mode 100644 packages/create-react-native-library/templates/native-view-new/react-native.config.js diff --git a/docs/pages/build.md b/docs/pages/build.md index b83458218..322d6bbd8 100644 --- a/docs/pages/build.md +++ b/docs/pages/build.md @@ -24,8 +24,6 @@ npx react-native-builder-bob@latest init This will ask you a few questions and add the required configuration and scripts for building the code. The code will be compiled automatically when the package is published. -> Note: the `init` command doesn't add the [`codegen` target](#codegen) yet. You can either add it manually or create a new library with `create-react-native-library`. - You can find details on what exactly it adds in the [Manual configuration](#manual-configuration) section. ## Manual configuration @@ -46,9 +44,7 @@ To configure your project manually, follow these steps: "output": "lib", "targets": [ ["module", { "esm": true }], - ["commonjs", { "esm": true }], "typescript", - "codegen" ] } ``` @@ -120,18 +116,6 @@ To configure your project manually, follow these steps: This makes sure that Jest doesn't try to run the tests in the generated files. -7. Configure [React Native Codegen](https://reactnative.dev/docs/the-new-architecture/what-is-codegen) - - If your library supports the [New React Native Architecture](https://reactnative.dev/architecture/landing-page), you should also configure Codegen. This is not required for libraries that only support the old architecture. - - You can follow the [Official Codegen Setup Guide](https://reactnative.dev/docs/the-new-architecture/using-codegen) to enable Codegen. - - It's also recommended to ship your Codegen generated scaffold code with your library since it has numerous benefits. To see the benefits and implement this behavior, you can see the [Official Codegen Shipping Guide](https://reactnative.dev/docs/the-new-architecture/codegen-cli#including-generated-code-into-libraries). - - See [How to opt-out of shipping the Codegen generated code](./faq.md#how-to-opt-out-of-shipping-codegen-generated-scaffold-code) if you don't want to ship the Codegen generated scaffold code. - - > Note: If you enable Codegen generated code shipping, React Native won't build the scaffold code automatically when you build your test app. You need to rebuild the codegen scaffold code manually each time you make changes to your spec. If you want to automate this process, you can create a new project with `create-react-native-library` and inspect the example app. - And we're done 🎉 ## Options @@ -281,9 +265,120 @@ If you need to support legacy setups that use `moduleResolution: node10` or `mod #### `codegen` -Enable generating the [React Native Codegen](https://reactnative.dev/docs/the-new-architecture/what-is-codegen) scaffold code, which is used with the New React Native Architecture. +Enable generating the [React Native Codegen](https://reactnative.dev/docs/the-new-architecture/what-is-codegen) scaffold code when building the library. + +If you use this `target`, you'll also want to use `"includesGeneratedCode": true` to ship the generated code with your library. Before you do so, make sure to [read the official docs](https://reactnative.dev/docs/the-new-architecture/codegen-cli#including-generated-code-into-libraries) to understand the advantages and tradeoffs of this approach. + +If you want to ship codegen generated code with your library, you can do the following steps to integrate it with the library's workflow: + +1. Add the `codegen` target to the `react-native-builder-bob` field in your `package.json` or `bob.config.js`: + + ```diff + "source": "src", + "output": "lib", + "targets": [ + // … + + "codegen" + ] + ``` + + This will enable the codegen script to run when you publish the library (if `bob build` is configured to be run on publish). + +2. Add `@react-native-community/cli` as a `devDependency` in your `package.json`: + + ```diff + "devDependencies": { + // … + + "@react-native-community/cli": "^x.x.x" + } + ``` + + For the `@react-native-community/cli` version, refer to the `example/package.json` file. The version should be the same as the one used in the `example` app. + +3. Add `"includesGeneratedCode": true` and `"outputDir"` to the `codegenConfig` field in your `package.json`: + + ```diff + "codegenConfig": { + // … + + "outputDir": { + + "ios": "ios/generated", + + "android": "android/generated" + + }, + + "includesGeneratedCode": true + } + ``` + +4. Update imports in your ios code to use the new paths for the generated code: + + - If you have a Turbo Module, replace `YourProjectNameSpec.h` with `YourProjectName/YourProjectNameSpec.h`: + + ```diff + - #import + + #import + ``` + + - If you have a Fabric View, replace `react/renderer/components/YourProjectNameViewSpec/` with `YourProjectName/`: + + ```diff + - #import + - #import + - #import + - #import + + #import + + #import + + #import + + #import + ``` + +5. Add a `react-native.config.js` at the root with the correct `cmakeListsPath`: + + ```js + /** + * @type {import('@react-native-community/cli-types').UserDependencyConfig} + */ + module.exports = { + dependency: { + platforms: { + android: { + cmakeListsPath: 'generated/jni/CMakeLists.txt', + }, + }, + }, + }; + ``` + + This makes sure that gradle will pickup the `CMakeLists.txt` file generated by the codegen script on Android. + +6. Add a gradle task to `example/android/app/build.gradle` to automatically run the codegen script when building the example app: + + ```groovy + tasks.register('invokeLibraryCodegen', Exec) { + workingDir "$rootDir/../../" + + def isWindows = System.getProperty('os.name').toLowerCase().contains('windows') + + if (isWindows) { + commandLine 'cmd', '/c', 'npx bob build --target codegen' + } else { + commandLine 'sh', '-c', 'npx bob build --target codegen' + } + } + + preBuild.dependsOn invokeLibraryCodegen + ``` + +7. Add a `pre_install` hook to `example/ios/Podfile` to automatically run the codegen script when installing pods: + + ```ruby + pre_install do |installer| + system("cd ../../ && npx bob build --target codegen") + end + ``` + + This will likely be inside the `target 'YourAppName' do` block. -You can ensure your Codegen generated scaffold code is stable through different React Native versions by shipping it with your library. You can find more in the [React Native Official Docs](https://reactnative.dev/docs/the-new-architecture/codegen-cli#including-generated-code-into-libraries). +And you're done! Make sure to run `pod install` in the `example/ios` folder and then run the example app to make sure everything works. #### `custom` diff --git a/docs/pages/faq.md b/docs/pages/faq.md index 4a3b0891e..9c9bfd8e0 100644 --- a/docs/pages/faq.md +++ b/docs/pages/faq.md @@ -124,46 +124,6 @@ For more accurate testing, there are various other approaches: You can find installation and usage instructions in the [Verdaccio documentation](https://verdaccio.org/docs/en/installation). -## How to opt out of shipping codegen generated code? - -We recommend shipping the generated scaffold code with your library due to [the benefits mentioned in React Native docs](https://reactnative.dev/docs/the-new-architecture/codegen-cli#including-generated-code-into-libraries). The new architecture libraries generated by `create-react-native-library` include the generated scaffold code by default. - -If you have a reason to not ship Codegen generated scaffold code with your library, you need do the following steps: - -1. Add `"includesGeneratedCode": false` to the `codegenConfig` field in your `package.json`: - - ```diff - "codegenConfig": { - // … - - "includesGeneratedCode": true - + "includesGeneratedCode": false - } - ``` - -2. Remove the [`codegen` target](#codegen) from the `react-native-builder-bob` field in your `package.json` or `bob.config.js`: - - ```diff - "source": "src", - "output": "lib", - "targets": [ - // … - - "codegen" - ] - ``` - -3. If you have an `exports` field in your `package.json`, ensure that it contains `./package.json`: - - ```diff - "exports": { - ".": { - // … - }, - + "./package.json": "./package.json" - }, - ``` - - This is required for React Native Codegen to read the `codegenConfig` field from your library's `package.json`. You can find the related issue [here](https://github.com/callstack/react-native-builder-bob/issues/637). - ## Users get a warning when they install my library If users are using Yarn 1, they may get a warning when installing your library: diff --git a/packages/create-react-native-library/src/exampleApp/addCodegenBuildScript.ts b/packages/create-react-native-library/src/exampleApp/addCodegenBuildScript.ts deleted file mode 100644 index c6ce2be84..000000000 --- a/packages/create-react-native-library/src/exampleApp/addCodegenBuildScript.ts +++ /dev/null @@ -1,58 +0,0 @@ -import path from 'path'; -import fs from 'fs-extra'; - -// This is added to the example app's build.gradle file to invoke codegen before every build -const GRADLE_INVOKE_CODEGEN_TASK = ` -// Run Codegen during development for the example app. -tasks.register('invokeLibraryCodegen', Exec) { - workingDir "$rootDir/../../" - def isWindows = System.getProperty('os.name').toLowerCase().contains('windows') - - if (isWindows) { - commandLine 'cmd', '/c', 'npx bob build --target codegen' - } else { - commandLine 'sh', '-c', 'npx bob build --target codegen' - } -} - -preBuild.dependsOn invokeLibraryCodegen -`; - -// You need to have the files before calling pod install otherwise they won't be registered in your pod. -// So we add a pre_install hook to the podfile that invokes codegen -const PODSPEC_INVOKE_CODEGEN_SCRIPT = ` - # Run Codegen during development for the example app. - pre_install do |installer| - system("cd ../../ && npx bob build --target codegen") - end -`; - -/** - * Codegen isn't invoked for libraries with `includesGeneratedCode` set to `true`. - * This patches the example app to invoke library codegen on every app build. - */ -export async function addCodegenBuildScript(libraryPath: string) { - const appBuildGradlePath = path.join( - libraryPath, - 'example', - 'android', - 'app', - 'build.gradle' - ); - const podfilePath = path.join(libraryPath, 'example', 'ios', 'Podfile'); - - // Add a gradle task that runs before every build - let appBuildGradle = (await fs.readFile(appBuildGradlePath)).toString(); - appBuildGradle += GRADLE_INVOKE_CODEGEN_TASK; - - await fs.writeFile(appBuildGradlePath, appBuildGradle); - - // Add a preinstall action to the podfile that invokes codegen - const podfile = (await fs.readFile(podfilePath)).toString().split('\n'); - const podfilePostInstallIndex = podfile.findIndex((line) => - line.includes('post_install do |installer|') - ); - podfile.splice(podfilePostInstallIndex, 0, PODSPEC_INVOKE_CODEGEN_SCRIPT); - - await fs.writeFile(podfilePath, podfile.join('\n')); -} diff --git a/packages/create-react-native-library/src/exampleApp/dependencies.ts b/packages/create-react-native-library/src/exampleApp/dependencies.ts index 1ed1e1a83..13d859a4b 100644 --- a/packages/create-react-native-library/src/exampleApp/dependencies.ts +++ b/packages/create-react-native-library/src/exampleApp/dependencies.ts @@ -1,6 +1,5 @@ import path from 'path'; import fs from 'fs-extra'; -import type { TemplateConfiguration } from '../template'; import sortObjectKeys from '../utils/sortObjectKeys'; type PackageJson = { @@ -9,8 +8,7 @@ type PackageJson = { export async function alignDependencyVersionsWithExampleApp( pkg: PackageJson, - folder: string, - config: TemplateConfiguration + folder: string ) { const examplePackageJson = await fs.readJSON( path.join(folder, 'example', 'package.json') @@ -23,16 +21,6 @@ export async function alignDependencyVersionsWithExampleApp( '@react-native/babel-preset', ]; - if ( - config.example === 'vanilla' && - (config.project.moduleConfig === 'turbo-modules' || - config.project.viewConfig === 'fabric-view') - ) { - // React Native doesn't provide the community CLI as a dependency. - // We have to read the version from the example app and put to the root package json - PACKAGES_TO_COPY.push('@react-native-community/cli'); - } - const devDependencies: Record = {}; PACKAGES_TO_COPY.forEach((name) => { diff --git a/packages/create-react-native-library/src/index.ts b/packages/create-react-native-library/src/index.ts index 1dce51722..e78403ca3 100644 --- a/packages/create-react-native-library/src/index.ts +++ b/packages/create-react-native-library/src/index.ts @@ -3,7 +3,6 @@ import kleur from 'kleur'; import ora from 'ora'; import path from 'path'; import yargs from 'yargs'; -import { addCodegenBuildScript } from './exampleApp/addCodegenBuildScript'; import { alignDependencyVersionsWithExampleApp } from './exampleApp/dependencies'; import generateExampleApp from './exampleApp/generateExampleApp'; import { printErrorHelp, printNextSteps, printUsedRNVersion } from './inform'; @@ -120,19 +119,7 @@ async function create(_argv: yargs.Arguments) { const rootPackageJson = await fs.readJson(path.join(folder, 'package.json')); if (config.example !== 'none') { - await alignDependencyVersionsWithExampleApp( - rootPackageJson, - folder, - config - ); - } - - if ( - config.example === 'vanilla' && - (config.project.moduleConfig === 'turbo-modules' || - config.project.viewConfig === 'fabric-view') - ) { - addCodegenBuildScript(folder); + await alignDependencyVersionsWithExampleApp(rootPackageJson, folder); } const libraryMetadata = createMetadata(answers); diff --git a/packages/create-react-native-library/templates/common-local/$package.json b/packages/create-react-native-library/templates/common-local/$package.json index 9970c8f8f..7325f243c 100644 --- a/packages/create-react-native-library/templates/common-local/$package.json +++ b/packages/create-react-native-library/templates/common-local/$package.json @@ -4,7 +4,7 @@ "description": "<%- project.description %>", "main": "src/index", "codegenConfig": { - "name": "RN<%- project.name -%><%- project.viewConfig !== null ? 'View': '' -%>Spec", + "name": "<%- project.name -%><%- project.viewConfig !== null ? 'View': '' -%>Spec", "type": <%- project.viewConfig !== null ? '"all"': '"modules"' %>, "jsSrcsDir": "src" }, diff --git a/packages/create-react-native-library/templates/common/$package.json b/packages/create-react-native-library/templates/common/$package.json index b9dfe1fb9..7efbe4122 100644 --- a/packages/create-react-native-library/templates/common/$package.json +++ b/packages/create-react-native-library/templates/common/$package.json @@ -171,9 +171,6 @@ "clean": "nitrogen/" } ], -<% } -%> -<% if (project.moduleConfig === 'turbo-modules' || project.viewConfig === 'fabric-view') { -%> - "codegen", <% } -%> [ "module", @@ -191,13 +188,9 @@ <% if (project.moduleConfig === 'turbo-modules' || project.viewConfig === 'fabric-view') { -%> }, "codegenConfig": { - "name": "RN<%- project.name -%><%- project.viewConfig !== null ? 'View': '' -%>Spec", + "name": "<%- project.name -%><%- project.viewConfig !== null ? 'View': '' -%>Spec", "type": "<%- project.viewConfig !== null ? 'all': 'modules' -%>", "jsSrcsDir": "src", - "outputDir": { - "ios": "ios/generated", - "android": "android/generated" - }, "android": { "javaPackageName": "com.<%- project.package %>" <% if (example === 'vanilla') { -%> @@ -208,9 +201,6 @@ "<%- project.name -%>View": "<%- project.name -%>View" } <% } -%> - }, - "includesGeneratedCode": true - <% } else { -%> } <% } -%> <% } -%> diff --git a/packages/create-react-native-library/templates/native-library-new/react-native.config.js b/packages/create-react-native-library/templates/native-library-new/react-native.config.js deleted file mode 100644 index 01104a6b5..000000000 --- a/packages/create-react-native-library/templates/native-library-new/react-native.config.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @type {import('@react-native-community/cli-types').UserDependencyConfig} - */ -module.exports = { - dependency: { - platforms: { - android: { - <%_ if (example === 'vanilla') { -%> - cmakeListsPath: 'generated/jni/CMakeLists.txt', - <%_ } else { -%> - cmakeListsPath: 'build/generated/source/codegen/jni/CMakeLists.txt', - <%_ } -%> - }, - }, - }, -}; diff --git a/packages/create-react-native-library/templates/native-view-new/react-native.config.js b/packages/create-react-native-library/templates/native-view-new/react-native.config.js deleted file mode 100644 index 01104a6b5..000000000 --- a/packages/create-react-native-library/templates/native-view-new/react-native.config.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @type {import('@react-native-community/cli-types').UserDependencyConfig} - */ -module.exports = { - dependency: { - platforms: { - android: { - <%_ if (example === 'vanilla') { -%> - cmakeListsPath: 'generated/jni/CMakeLists.txt', - <%_ } else { -%> - cmakeListsPath: 'build/generated/source/codegen/jni/CMakeLists.txt', - <%_ } -%> - }, - }, - }, -}; diff --git a/packages/create-react-native-library/templates/objc-library/ios/{%- project.name %}.h b/packages/create-react-native-library/templates/objc-library/ios/{%- project.name %}.h index cd2f0acd7..cb2be40fe 100644 --- a/packages/create-react-native-library/templates/objc-library/ios/{%- project.name %}.h +++ b/packages/create-react-native-library/templates/objc-library/ios/{%- project.name %}.h @@ -1,4 +1,4 @@ -#import <<%- project.name -%>/RN<%- project.name -%>Spec.h> +#import <<%- project.name -%>Spec/<%- project.name -%>Spec.h> @interface <%- project.name -%> : NSObject Spec> diff --git a/packages/create-react-native-library/templates/objc-view-new/ios/{%- project.name %}View.mm b/packages/create-react-native-library/templates/objc-view-new/ios/{%- project.name %}View.mm index 4dd543215..187c18d2e 100644 --- a/packages/create-react-native-library/templates/objc-view-new/ios/{%- project.name %}View.mm +++ b/packages/create-react-native-library/templates/objc-view-new/ios/{%- project.name %}View.mm @@ -1,9 +1,9 @@ #import "<%- project.name -%>View.h" -#import <<%- project.name -%>/ComponentDescriptors.h> -#import <<%- project.name -%>/EventEmitters.h> -#import <<%- project.name -%>/Props.h> -#import <<%- project.name -%>/RCTComponentViewHelpers.h> +#import ViewSpec/ComponentDescriptors.h> +#import ViewSpec/EventEmitters.h> +#import ViewSpec/Props.h> +#import ViewSpec/RCTComponentViewHelpers.h> #import "RCTFabricComponentsPlugins.h"