From 29dfdaba33c0a3bd2df15532df1ecb5a14382dce Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Wed, 25 Jan 2023 16:49:22 +0000 Subject: [PATCH 01/25] add RFC for reactNativeMeta --- proposals/0011-introduce-reactNativeMeta.md | 148 ++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 proposals/0011-introduce-reactNativeMeta.md diff --git a/proposals/0011-introduce-reactNativeMeta.md b/proposals/0011-introduce-reactNativeMeta.md new file mode 100644 index 00000000..a7bb4f63 --- /dev/null +++ b/proposals/0011-introduce-reactNativeMeta.md @@ -0,0 +1,148 @@ +--- +title: Introducing `reactNativeMeta` to `package.json`, for RN specific metadata +author: + - Lorenzo Sciandra +date: 25-01-2021 +--- + +# RFC0011: Introducing `reactNativeMeta` to `package.json`, for RN specific metadata + +## Summary + +This RFC wants to introduce and formally discuss a new section for the package.json of a react-native project (app or library), called `reactNativeMeta`. + +This new section will allow developer to express certain characteristics of their code in a more formalized manner, that can easily used by tooling to act towards the code accordingly. + +**Note well**: this is but the first draft of the actual shape and fields. The goal of this draft is to collect feedback and help finalize the spec for this section. + +## Basic examples + +This is how this section would look like... + +...for a library: + +```json +{ + "name": "@rnx-kit/metro-serializer", + "version": "1.0.11", + ..., + "reactNativeMeta": { + "version": "2.3", + "type": "library", + "features": { + "fabric": "true", + "android": { + "turbomodules": "true", + }, + }, + "requirements": { + "react-native": ">= 0.71.0", + "expo": ">= 46.0.0", + "expo-modules-core": "1.2.0" + } + ... +} +``` + +...for an app: + +```json +{ + "name": "contoso", + "version": "2.0.3", + ..., + "reactNativeMeta": { + "version": "1.0", + "type": "app", + "features": { + "newArchEnabled": "true", + "android": { + "codegen": "true", + "turbomodules": "true", + }, + // Future example + // "metroExportsMode": "strict" + }, + ... + } +``` + +## Motivation + +The conversation that originated this idea was [an ask from the Meta team](https://github.com/microsoft/rnx-kit/issues/1863) to have `align-deps` help developers in the community migrating to the new architecture by providing insights into which libraries would have Fabric/TM support. Very quickly it became clear that there is currently no straightforward way to know if a library indeed has that support. + +A similar problem was also encountered by [React Native Directory](https://reactnative.directory/), that tried to [programmatically infer](https://github.com/react-native-community/directory/pull/870) support via the presence of certain fields in the package.json of a library - this because one can get access to the manifest file via npm without having to download the entire node_module. + +It became clear that there was a need to be able to communicate and parse certain information about a react-native library via its `package.json`, which led to [a meeting](https://github.com/microsoft/rnx-kit/discussions/2125) between Meta, Microsoft and Expo to figure out a common solution. + +This RFC is the result of that meeting: a new section of the `package.json`, called `reactNativeMeta`, to contain certain metadata about a react-native project - it being either a library or an app. + +There are a few areas that this section could express in a consistent manner; here's a list of the ones we could think of (in no particular order): + +- if a library supports the new architecture, turbo modules or fabric or both +- which versions of react-native a library supports in a given release +- if an app has opted in into Fabric, TM, or both +- if an app is using Hermes or another js engine + +Sidenote: the name `reactNativeMeta` was chosen to mirror the camel case pattern used by other fields such as `peerDependenciesMeta`. + +## Detailed design + +This is the section that in this first draft will need to be properly finalised. For now, here's what we envisioned: + +```js +reactNativeMeta: { + version: string, // ex. 1.0.0 - we know that this spec might evolve in the future + type: string; // "app" or "library" + features: { + // can be global... + newArchEnabled: boolean, + hermesEnabled: boolean, + // ...or platform specific + android: { + newArchEnabled: boolean, + fabric: boolean, + turbomodules: boolean, + hermesEnabled: boolean, + }, + ios: { + ... // same as android + }, + macos: { + ... // same as android + }, + windows: { + ... // same as android + }, + // more platforms, params - add a comment here + }, + // this section would most likely only be relevant for libraries + requirements: { + react-native: string, // semver compliant range of versions of RN supported + expo: string, // semver compliant range of versions of Expo supported (if any) + } +} +``` + +## Drawbacks + +The main drawback is that we'd need to convince developers to start using it, so we'd have to expect a phase in which only a part of the userbase has this section configured and tool developers need to account for the section missing entirely. + +## Alternatives + +This section is, in itself, fully original and new: there are other files that are used to define configurations for various aspect of react-native based projects (such as `react-native.config.js`, `app.json`, Expo's `app.config.js`, `expo-module.config.json`) but by design this section does **not** overlap with any of them. Explicitly, this one has the purpose of being filled only with metadata to be read by package managers and other tools. + +## Adoption strategy + +Once the v1.0.0 version of the shape is finalised, we can start integrating it across the board in tools such has `align-deps` and the RNDirectory, and in the react-native tools themselves. This first wave of support would have to be coordinated around a certain release that lives in RN's main branch, such as now it'd be introduced in 0.72 (which branch has not been cut). + +After adding this first wave of support, and related documentation, we could more broadly communicate to the community and the maintainers how adding this section would benefit them too. Especially for library maintainers we'll have another RFC coming up soon to describe how we are planning to better communicate this. + +## How we teach this + +- add to core's documentation (which should happen anyway if we start using it for flags for new arch configuration) +- have RNDirectory and align-deps mention a call to action to introduce this + +## Unresolved questions + +v1.0.0 of the shape needs to be finalized. From 0f991f29359543cef7495790517202218f45bb9e Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Wed, 25 Jan 2023 16:56:33 +0000 Subject: [PATCH 02/25] quick cleanup --- proposals/0011-introduce-reactNativeMeta.md | 66 ++++++++++----------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/proposals/0011-introduce-reactNativeMeta.md b/proposals/0011-introduce-reactNativeMeta.md index a7bb4f63..8e8e0c2e 100644 --- a/proposals/0011-introduce-reactNativeMeta.md +++ b/proposals/0011-introduce-reactNativeMeta.md @@ -23,24 +23,24 @@ This is how this section would look like... ```json { - "name": "@rnx-kit/metro-serializer", - "version": "1.0.11", - ..., - "reactNativeMeta": { - "version": "2.3", - "type": "library", - "features": { - "fabric": "true", + "name": "@rnx-kit/metro-serializer", + "version": "1.0.11", + ..., + "reactNativeMeta": { + "version": "2.3", + "type": "library", + "features": { + "fabric": "true", "android": { "turbomodules": "true", - }, - }, - "requirements": { - "react-native": ">= 0.71.0", - "expo": ">= 46.0.0", - "expo-modules-core": "1.2.0" + }, + }, + "requirements": { + "react-native": ">= 0.71.0", + "expo": ">= 46.0.0", + "expo-modules-core": "1.2.0" + } } - ... } ``` @@ -48,23 +48,23 @@ This is how this section would look like... ```json { - "name": "contoso", - "version": "2.0.3", - ..., - "reactNativeMeta": { + "name": "contoso", + "version": "2.0.3", + ..., + "reactNativeMeta": { "version": "1.0", "type": "app", "features": { - "newArchEnabled": "true", - "android": { - "codegen": "true", - "turbomodules": "true", - }, - // Future example - // "metroExportsMode": "strict" + "newArchEnabled": "true", + "android": { + "codegen": "true", + "turbomodules": "true", + }, + // Future example + // "metroExportsMode": "strict" }, ... - } + } ``` ## Motivation @@ -97,18 +97,18 @@ reactNativeMeta: { features: { // can be global... newArchEnabled: boolean, - hermesEnabled: boolean, + jsEngine: string, // ex. "hermes", "jsc", "V8"... // ...or platform specific android: { newArchEnabled: boolean, fabric: boolean, turbomodules: boolean, - hermesEnabled: boolean, + jsEngine: string, }, - ios: { + iOs: { ... // same as android }, - macos: { + macOs: { ... // same as android }, windows: { @@ -118,7 +118,7 @@ reactNativeMeta: { }, // this section would most likely only be relevant for libraries requirements: { - react-native: string, // semver compliant range of versions of RN supported + "react-native": string, // semver compliant range of versions of RN supported expo: string, // semver compliant range of versions of Expo supported (if any) } } @@ -130,7 +130,7 @@ The main drawback is that we'd need to convince developers to start using it, so ## Alternatives -This section is, in itself, fully original and new: there are other files that are used to define configurations for various aspect of react-native based projects (such as `react-native.config.js`, `app.json`, Expo's `app.config.js`, `expo-module.config.json`) but by design this section does **not** overlap with any of them. Explicitly, this one has the purpose of being filled only with metadata to be read by package managers and other tools. +`reactNativeMeta` is, by design, fully original and new: there are other files that are used to define configurations for various aspect of react-native based projects (such as `react-native.config.js`, `app.json`, Expo's `app.config.js`, `expo-module.config.json`) but this one does **not** overlap with any of them. Explicitly, this one has the purpose of being filled only with metadata to be read by package managers and other tools. ## Adoption strategy From bcf35765e941d535c3079c0f5fa899297fa73fd7 Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Wed, 25 Jan 2023 16:57:59 +0000 Subject: [PATCH 03/25] a bit more cleanup --- ...reactNativeMeta.md => 0012-introduce-reactNativeMeta.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{0011-introduce-reactNativeMeta.md => 0012-introduce-reactNativeMeta.md} (98%) diff --git a/proposals/0011-introduce-reactNativeMeta.md b/proposals/0012-introduce-reactNativeMeta.md similarity index 98% rename from proposals/0011-introduce-reactNativeMeta.md rename to proposals/0012-introduce-reactNativeMeta.md index 8e8e0c2e..9f8277da 100644 --- a/proposals/0011-introduce-reactNativeMeta.md +++ b/proposals/0012-introduce-reactNativeMeta.md @@ -5,7 +5,7 @@ author: date: 25-01-2021 --- -# RFC0011: Introducing `reactNativeMeta` to `package.json`, for RN specific metadata +# RFC0012: Introducing `reactNativeMeta` to `package.json`, for RN specific metadata ## Summary @@ -32,8 +32,8 @@ This is how this section would look like... "features": { "fabric": "true", "android": { - "turbomodules": "true", - }, + "turbomodules": "true", + }, }, "requirements": { "react-native": ">= 0.71.0", From 23a53665cdb6b0ae42e485a0f0f95c261db5b24e Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Fri, 27 Jan 2023 16:09:03 +0000 Subject: [PATCH 04/25] meta->metadata --- ...d => 0012-introduce-reactNativeMetadata.md} | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) rename proposals/{0012-introduce-reactNativeMeta.md => 0012-introduce-reactNativeMetadata.md} (84%) diff --git a/proposals/0012-introduce-reactNativeMeta.md b/proposals/0012-introduce-reactNativeMetadata.md similarity index 84% rename from proposals/0012-introduce-reactNativeMeta.md rename to proposals/0012-introduce-reactNativeMetadata.md index 9f8277da..482ba6e4 100644 --- a/proposals/0012-introduce-reactNativeMeta.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -1,15 +1,15 @@ --- -title: Introducing `reactNativeMeta` to `package.json`, for RN specific metadata +title: Introducing `reactNativeMetadata` to `package.json`, for RN specific metadata author: - Lorenzo Sciandra date: 25-01-2021 --- -# RFC0012: Introducing `reactNativeMeta` to `package.json`, for RN specific metadata +# RFC0012: Introducing `reactNativeMetadata` to `package.json`, for RN specific metadata ## Summary -This RFC wants to introduce and formally discuss a new section for the package.json of a react-native project (app or library), called `reactNativeMeta`. +This RFC wants to introduce and formally discuss a new section for the package.json of a react-native project (app or library), called `reactNativeMetadata`. This new section will allow developer to express certain characteristics of their code in a more formalized manner, that can easily used by tooling to act towards the code accordingly. @@ -26,7 +26,7 @@ This is how this section would look like... "name": "@rnx-kit/metro-serializer", "version": "1.0.11", ..., - "reactNativeMeta": { + "reactNativeMetadata": { "version": "2.3", "type": "library", "features": { @@ -51,7 +51,7 @@ This is how this section would look like... "name": "contoso", "version": "2.0.3", ..., - "reactNativeMeta": { + "reactNativeMetadata": { "version": "1.0", "type": "app", "features": { @@ -75,7 +75,7 @@ A similar problem was also encountered by [React Native Directory](https://react It became clear that there was a need to be able to communicate and parse certain information about a react-native library via its `package.json`, which led to [a meeting](https://github.com/microsoft/rnx-kit/discussions/2125) between Meta, Microsoft and Expo to figure out a common solution. -This RFC is the result of that meeting: a new section of the `package.json`, called `reactNativeMeta`, to contain certain metadata about a react-native project - it being either a library or an app. +This RFC is the result of that meeting: a new section of the `package.json`, called `reactNativeMetadata`, to contain certain metadata about a react-native project - it being either a library or an app. There are a few areas that this section could express in a consistent manner; here's a list of the ones we could think of (in no particular order): @@ -84,14 +84,14 @@ There are a few areas that this section could express in a consistent manner; he - if an app has opted in into Fabric, TM, or both - if an app is using Hermes or another js engine -Sidenote: the name `reactNativeMeta` was chosen to mirror the camel case pattern used by other fields such as `peerDependenciesMeta`. +Sidenote: the name `reactNativeMetadata` was chosen to mirror the camel case pattern used by other fields such as `peerDependenciesMeta`. ## Detailed design This is the section that in this first draft will need to be properly finalised. For now, here's what we envisioned: ```js -reactNativeMeta: { +reactNativeMetadata: { version: string, // ex. 1.0.0 - we know that this spec might evolve in the future type: string; // "app" or "library" features: { @@ -130,7 +130,7 @@ The main drawback is that we'd need to convince developers to start using it, so ## Alternatives -`reactNativeMeta` is, by design, fully original and new: there are other files that are used to define configurations for various aspect of react-native based projects (such as `react-native.config.js`, `app.json`, Expo's `app.config.js`, `expo-module.config.json`) but this one does **not** overlap with any of them. Explicitly, this one has the purpose of being filled only with metadata to be read by package managers and other tools. +`reactNativeMetadata` is, by design, fully original and new: there are other files that are used to define configurations for various aspect of react-native based projects (such as `react-native.config.js`, `app.json`, Expo's `app.config.js`, `expo-module.config.json`) but this one does **not** overlap with any of them. Explicitly, this one has the purpose of being filled only with metadata to be read by package managers and other tools. ## Adoption strategy From dba57a4bf90da1bbf65454ed3e4b98fed5398d25 Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Fri, 27 Jan 2023 16:11:36 +0000 Subject: [PATCH 05/25] capitalization --- proposals/0012-introduce-reactNativeMetadata.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0012-introduce-reactNativeMetadata.md b/proposals/0012-introduce-reactNativeMetadata.md index 482ba6e4..ebbc0818 100644 --- a/proposals/0012-introduce-reactNativeMetadata.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -105,10 +105,10 @@ reactNativeMetadata: { turbomodules: boolean, jsEngine: string, }, - iOs: { + ios: { ... // same as android }, - macOs: { + macos: { ... // same as android }, windows: { From 2af85cd8a03d6dd4715a2564df02a77202657cfb Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Fri, 27 Jan 2023 16:13:59 +0000 Subject: [PATCH 06/25] a bit more details --- proposals/0012-introduce-reactNativeMetadata.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0012-introduce-reactNativeMetadata.md b/proposals/0012-introduce-reactNativeMetadata.md index ebbc0818..07640c6e 100644 --- a/proposals/0012-introduce-reactNativeMetadata.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -134,13 +134,14 @@ The main drawback is that we'd need to convince developers to start using it, so ## Adoption strategy -Once the v1.0.0 version of the shape is finalised, we can start integrating it across the board in tools such has `align-deps` and the RNDirectory, and in the react-native tools themselves. This first wave of support would have to be coordinated around a certain release that lives in RN's main branch, such as now it'd be introduced in 0.72 (which branch has not been cut). +Once the v1.0.0 version of the shape is finalised, we can start integrating it across the board in tools such has `align-deps` and the RNDirectory, and in the react-native tools themselves. This first wave of support would have to be coordinated around a certain release that lives in RN's main branch; for example, if we were to introduce it now, it'd be in 0.72 (which branch has not been cut) via the app template. After adding this first wave of support, and related documentation, we could more broadly communicate to the community and the maintainers how adding this section would benefit them too. Especially for library maintainers we'll have another RFC coming up soon to describe how we are planning to better communicate this. ## How we teach this - add to core's documentation (which should happen anyway if we start using it for flags for new arch configuration) +- add to the templates (both app and library) - have RNDirectory and align-deps mention a call to action to introduce this ## Unresolved questions From 8ccc3dfe6711dfe22fcad7cebf625125bffa2b07 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Fri, 17 Mar 2023 19:13:02 +0000 Subject: [PATCH 07/25] Reword the rfc by @cortinico to reduce scope --- .../0012-introduce-reactNativeMetadata.md | 384 +++++++++++++----- 1 file changed, 282 insertions(+), 102 deletions(-) diff --git a/proposals/0012-introduce-reactNativeMetadata.md b/proposals/0012-introduce-reactNativeMetadata.md index 07640c6e..36660312 100644 --- a/proposals/0012-introduce-reactNativeMetadata.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -1,149 +1,329 @@ --- -title: Introducing `reactNativeMetadata` to `package.json`, for RN specific metadata +title: [DRAFT] Introducing `reactNativeManifest` to `package.json` for React Native specific metadata author: - Lorenzo Sciandra + - Nicola Corti date: 25-01-2021 --- -# RFC0012: Introducing `reactNativeMetadata` to `package.json`, for RN specific metadata +# RFC0588: [DRAFT] Introducing `reactNativeManifest` to `package.json` for React Native specific metadata ## Summary -This RFC wants to introduce and formally discuss a new section for the package.json of a react-native project (app or library), called `reactNativeMetadata`. +> **Warning** ⚠️ This is the initial draft of this proposal. The goal of this draft is to collect feedback and help finalize the spec for this section. -This new section will allow developer to express certain characteristics of their code in a more formalized manner, that can easily used by tooling to act towards the code accordingly. +This RFC introduces and formally discuss the concept of _React Native Manifest_, a set of metadata about a React Native project (either app or library). This metadata can be consumed by tooling to enhance the developer experience and support the React Native ecosystem. -**Note well**: this is but the first draft of the actual shape and fields. The goal of this draft is to collect feedback and help finalize the spec for this section. +Practically, this RFC proposes to add a `reactNativeManifest` section to the `package.json` file of a React Native app or library. +This new section will allow developers to follow a declarative approach to express capabilities of apps and libraries in a more formalized manner. -## Basic examples +## Rationale -This is how this section would look like... +The need to specify **capabilities and metadata** for a react-native library is inherent to the React Native ecosystem. -...for a library: +As of today, there is no simple programmatic way to know if a NPM package is a React Native library. Good indicators could be: +* Presence of a `react-native:*` dependency in the `peerDependencies` section of the `package.json` ([example](https://github.com/RonRadtke/react-native-blob-util/blob/80628d418a5c81439dc2c1a1f05fae6406f0ba7f/package.json#L46C10-L49)) +* Presence of React Native specific files such as `react-native.config.js` files +Those indicators are all non-exhaustive. + +Similarly, there is no easy way to understand if a library is exposing a native module or not. +The React Native CLI is facing this difficulty and tries to circumvent it by inspecting the native code and search for subclasses of `*ReactPackage` using complex regular expressions ([example](https://github.com/react-native-community/cli/blob/605c542d18efcb02f217d3c85726fa73a79054c2/packages/cli-platform-android/src/config/findPackageClassName.ts#L35)) due to the lack of a declarative way to express this information. + +Another practical examples of this need are the [React Native Directory](https://reactnative.directory/) attempting to [programmatically infer](https://github.com/react-native-community/directory/pull/870) New Architecture support from the presence of the `codegenConfig` fields in the `package.json` of a library. The `codegenConfig` filed is not intended to carry semantic information about New Architecture support, and is definitely a non-exhaustive solution for this problem. + +While we can continue to build tooling that tries to infer metadata and capabilities of a library, providing a declarative way to declared such capabilites provides a series of benefit: +* **predictability**: developers can easily understand what a library is capable of doing and if such capability matches their app. +* **tooling**: tooling can easily consume this information and provide a better experience to developers. +* **single source of thruth**: a single manifest will act as a source of truth for all those capabilities and flag definition, that can easily be consumed by different platforms to provide a unified way to enable/disable capabilities. +* **verifiability**: the manifest can be easily verified & linted by tooling to ensure that app & library are not using an incompatible set of compatibilites. Similarly, the JSON schema we publish can be used to validate the `reactNativeManifest` section. + +## Use Cases + +This section lists the use case we envision for the `reactNativeManifest` section. + +### Declaring the type of a project + +For the sake of this RFC, we consider a React Native project as either an app or a library. +While an app project can contain native modules and components, it's **not** possible for a app to "consume" another app as a library as this would break the native build system invariants (i.e. a single `AndroidManifest.xml` and `Info.plist` file per app, etc.). If an app projects wishes to distribute some of its native modules or components, they need to be extracted into a library project. + +We're proposing to declare if a project is either a library or an app by adding the `type` field (as `"app"` or `"library"`) to the `reactNativeManifest` section of the `package.json` file. + +```json +{ + "reactNativeManifest": { + "type": "library" + } +} +``` + +### New Architecture support + +One of the primary driver of this proposal is the [New React Native Architecture support](https://reactnative.dev/docs/the-new-architecture/landing-page). Specifically we believe that the ecosystem is currently affected by: +* Lack of a declarative way to define if a library is compatible with New Architecture + * As described in the [rationale](#rationale), tooling needs to infer this information from the presence of the `codegenConfig` field in the `package.json` of a library. Libraries might decide to don't use codegen and still be compatible with New Architecture, so this method is not exhaustive. + * The alternative at this stage is to inspect the code and check the API callsites if they are New Architecture or Old Architecture compatible. +* Lack of a declarative way to enable New Architecture on both platforms + * Currently New Architecture is enabled by a Gradle Property `newArchEnabled` on Android in the `gradle.properties` file and by invokign `RCT_NEW_ARCH_ENABLED pod install` on iOS. + * As of today, there is no way to enable New Architecture for an app project for both platforms + * Moreover, the file vs environment variable leads to a scenario where you can't know if a app supports New Architecture or not by inspecting the code (as New Architecture support is known at `pod install` time and is not codified in the codebase) + +Therefore we propose to add the `newArch` section to the `reactNativeManifest.capabilities` of **both apps and libraries** with the following semantic: +* For Apps: `newArch.enabled==true` implies that the app wants to use the New Architecture. +* For Libraries: `newArch.enabled==true` implies that the library is compatible with the New Architecture. + +Tools can be built on top of this information to check that a app with `newArch.enabled==true` is not accepting libraries with `newArch.enabled==false` and warning against library that don't have the key specified. + +The setup would look as follows for both apps and libraries: + +```json +{ + "reactNativeManifest": { + "capabilities": { + "newArch": { + "enabled": true + } + } + } +} +``` + +This section will allows also for split configuration between platforms: + +```json +{ + "reactNativeManifest": { + "android": { + "capabilities": { + "newArch": { + "enabled": true + } + } + }, + "ios": { + "capabilities": { + "newArch": { + "enabled": true + } + } + } + } +} +``` + +#### `codegenConfig` support + +Similarly to New Architecture support metadata, the `codegenConfig` is a key metadata of the New Architecture build pipeline. +Currently the `codegenConfig` is a **top-level** key in the `package.json` of a project. + +We propose to move this key under the `reactNativeManifest.capabilites` section as follows: +```json +{ + "reactNativeManifest": { + "capabilities": { + "codegenConfig": { + "name": "MyLib", + "type": "all", + "jsSrcsDir": ".", + "android": { + "javaPackageName": "com.example.mypackage" + } + } + } + } +} +``` + +### Hermes Support + +Similarly to New Architecture support, the current way to enable/disable the Hermes engine is toggled by using a Gradle Property `hermesEnabled` on Android and changing the `:hermes_enabled` property in the Podfile. + +We propose to add the `hermes` section under the `reactNativeManifest.capabilities` section as follows: ```json { - "name": "@rnx-kit/metro-serializer", - "version": "1.0.11", - ..., - "reactNativeMetadata": { - "version": "2.3", - "type": "library", - "features": { - "fabric": "true", - "android": { - "turbomodules": "true", - }, - }, - "requirements": { - "react-native": ">= 0.71.0", - "expo": ">= 46.0.0", - "expo-modules-core": "1.2.0" + "reactNativeManifest": { + "capabilities": { + "hermes": { + "enabled": true + } + } + } +} +``` + +which will also allow for split configuration between platforms as follows: + +```json +{ + "reactNativeManifest": { + "android": { + "capabilities": { + "hermes": { + "enabled": true + } + } + }, + "ios": { + "capabilities": { + "hermes": { + "enabled": true } + } } + } } ``` -...for an app: +### React Native Release feature flagging + +We currently have a numer of different files where capabilities of React Native can be toggled to enable/disable features in the runtime. +For example we have: +* [android/gradle.properties](https://github.com/facebook/react-native/blob/main/packages/react-native/template/android/gradle.properties) to specify key-value build time properties for Android +* [android/app/build.gradle](https://github.com/facebook/react-native/blob/main/packages/react-native/template/android/app/build.gradle) to specify build time configuration for Android +* [ReactNativeFeatureFlags.js](https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/ReactNative/ReactNativeFeatureFlags.js) which contains runtime feature toggles for the Javascript layer. +* [ReactFeatureFlags.java](https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java) which contains runtime feature toggles for Android. +* [ReactNativeConfig.cpp](https://github.com/facebook/react-native/blob/main/packages/react-native/ReactCommon/react/config/ReactNativeConfig.cpp) which contains runtime feature toggles for the C++ layer. + +Most of those config flags are undocumented and they're not verified for invalid configuration. Specifically, some feature flags require to be toggled on multiple layers, and it's easy to forget to toggle one of them. Invalid combinations of those flags could lead to unexpected runtime behavior. + +We propose to define `reactNativeManifest` as the **preferred entry point** for all the user facing entry points. All the tooling should be adapted to consume the information from the `reactNativeManifest` section and have sensible defaults defined by the tooling itself. + +It's outside the scope of this RFC to declare all the new sections we intend to add, and we defer to the [Future proof and extensibility](#future-proof-and-extensibility) section for the process on how to include a new key to the `reactNativeManifest` section. + +## Excluded use cases + +This section lists the use cases where we believe the `reactNativeManifest` section should **not** be used. + +### React Native version support + +For the time being, we're not planning to add a `version` section to the `reactNativeManifest` section for libraries. The rationale is that we believe that the `react-native` version should be specified in either the `peerDependencies` or the `engines` section of the `package.json` of the project. This follows the idiomatic way to specify the version of a dependency in a NodeJS project. + +### TurboModule/Fabric toggles + +At the time of writing, we prefer to don't offer a dedicated section to toggle Fabric/TurboModule capability inside `reactNativeManifest`. The rationale is that we believe that the `newArch` section is sufficient to expose the New Architecture to users. +Selectively toggling Fabric/TurboModule is a more advanced feature. We believe we'll still be offering a more advanced way to feature toggle such infrastructure pieces, but at the current state, our preference is to not expose such capability in the top level `reactNativeManifest` section. + +## Proposed Tooling + +This section lists the tooling we believe could be built on top of `reactNativeManifest` to improve the developer experience. + +### `align-deps` support + +This RFC originated from a [conversation between Meta and Microsoft](https://github.com/microsoft/rnx-kit/issues/1863) to use `align-deps` as key feature in the New Architecture rollout support, in helping developers understanding if a library they're using is New Architecture compatible or not. Very quickly it became clear that there is currently no straightforward way to know if a library indeed has that support. + +Therefore we believe `align-deps` can benefit from this information and be extended to offer New Architecture support information. +### React Native CLI support + +Ideally the CLI (or any other build tool) should be able to consume the `reactNativeManifest` section and warn at build time if a configuration is invalid (either by having an invalid value or by having a mixture of invalid configurations). + +It's outside the scope of this RFC to delve into details of which tool will implement which logic and we defer this to a future discussion. + +### React Native Directory + +The React Native direcotry can easily consume the `reactNativeManifest` by: +1. Query the NPM registry with [their API](https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md) to obtain the repository information. +2. Query the repository information to obtain the `packge.json` and read the `reactNativeManifest` section. + +## Future proof and extensibility + +We believe that the `reactNativeManifest` section should be extensible as we believe new capabilities and features can be added there. +Specifically, we envision to evolve `reactNativeManifest` or `reactNativeManifest.capabilites` as follows: + +1. Each addition of a new capability [requires a new RFC](https://github.com/react-native-community/discussions-and-proposals#proposals) to be approved. +2. Each RFC should define a new section in the `reactNativeManifest` with what's the intended use case. +3. Each RFC should define what are the sensible defaults and should refrain from breaking changes. +4. Each RFC should define the deprecation strategy for previous flags/features related to the same capability. + +## Complete Example + +Here we present a full example of how a `reactNativeManifest` section could look like for a library and an app. + +For an app the section will look as follows: + ```json { - "name": "contoso", - "version": "2.0.3", - ..., - "reactNativeMetadata": { + "name": "my-awesome-app", + "version": "1.2.3", + "reactNativeManifest": { "version": "1.0", "type": "app", - "features": { - "newArchEnabled": "true", + "capabilities": { + "hermes": { + "enabled": true + }, + "newArch": { + "enabled": true + }, + "codegenConfig": { + "name": "AppSpecs", + "type": "all", + "jsSrcsDir": ".", "android": { - "codegen": "true", - "turbomodules": "true", - }, - // Future example - // "metroExportsMode": "strict" - }, - ... + "javaPackageName": "com.facebook.fbreact.specs" + } + } } + } +} ``` -## Motivation - -The conversation that originated this idea was [an ask from the Meta team](https://github.com/microsoft/rnx-kit/issues/1863) to have `align-deps` help developers in the community migrating to the new architecture by providing insights into which libraries would have Fabric/TM support. Very quickly it became clear that there is currently no straightforward way to know if a library indeed has that support. - -A similar problem was also encountered by [React Native Directory](https://reactnative.directory/), that tried to [programmatically infer](https://github.com/react-native-community/directory/pull/870) support via the presence of certain fields in the package.json of a library - this because one can get access to the manifest file via npm without having to download the entire node_module. - -It became clear that there was a need to be able to communicate and parse certain information about a react-native library via its `package.json`, which led to [a meeting](https://github.com/microsoft/rnx-kit/discussions/2125) between Meta, Microsoft and Expo to figure out a common solution. - -This RFC is the result of that meeting: a new section of the `package.json`, called `reactNativeMetadata`, to contain certain metadata about a react-native project - it being either a library or an app. - -There are a few areas that this section could express in a consistent manner; here's a list of the ones we could think of (in no particular order): - -- if a library supports the new architecture, turbo modules or fabric or both -- which versions of react-native a library supports in a given release -- if an app has opted in into Fabric, TM, or both -- if an app is using Hermes or another js engine - -Sidenote: the name `reactNativeMetadata` was chosen to mirror the camel case pattern used by other fields such as `peerDependenciesMeta`. - -## Detailed design - -This is the section that in this first draft will need to be properly finalised. For now, here's what we envisioned: - -```js -reactNativeMetadata: { - version: string, // ex. 1.0.0 - we know that this spec might evolve in the future - type: string; // "app" or "library" - features: { - // can be global... - newArchEnabled: boolean, - jsEngine: string, // ex. "hermes", "jsc", "V8"... - // ...or platform specific - android: { - newArchEnabled: boolean, - fabric: boolean, - turbomodules: boolean, - jsEngine: string, - }, - ios: { - ... // same as android - }, - macos: { - ... // same as android - }, - windows: { - ... // same as android - }, - // more platforms, params - add a comment here - }, - // this section would most likely only be relevant for libraries - requirements: { - "react-native": string, // semver compliant range of versions of RN supported - expo: string, // semver compliant range of versions of Expo supported (if any) +For a library the section will look as follows: + +```json +{ + "name": "my-awesome-library", + "version": "1.2.3", + "reactNativeManifest": { + "version": "1.0", + "type": "library", + "capabilities": { + "hermes": { + "enabled": true + }, + "newArch": { + "enabled": true + }, + "codegenConfig": { + "name": "LibrarySpecs", + "type": "all", + "jsSrcsDir": ".", + "android": { + "javaPackageName": "com.facebook.fbreact.specs" + } + } } + } } ``` -## Drawbacks +### JSON Schema -The main drawback is that we'd need to convince developers to start using it, so we'd have to expect a phase in which only a part of the userbase has this section configured and tool developers need to account for the section missing entirely. +The JSON Schema will be published as soon as we find an agreement on the key names and semantics. ## Alternatives -`reactNativeMetadata` is, by design, fully original and new: there are other files that are used to define configurations for various aspect of react-native based projects (such as `react-native.config.js`, `app.json`, Expo's `app.config.js`, `expo-module.config.json`) but this one does **not** overlap with any of them. Explicitly, this one has the purpose of being filled only with metadata to be read by package managers and other tools. +`reactNativeManifest` is, by design, fully original and new: there are other files that are used to define configurations for various aspect of react-native based projects (such as `react-native.config.js`, `app.json`, Expo's `app.config.js`, `expo-module.config.json`) but this one does **not** overlap with any of them. Explicitly, this one has the purpose of being filled only with capabilities and metadata to be read by package managers and build tools. ## Adoption strategy -Once the v1.0.0 version of the shape is finalised, we can start integrating it across the board in tools such has `align-deps` and the RNDirectory, and in the react-native tools themselves. This first wave of support would have to be coordinated around a certain release that lives in RN's main branch; for example, if we were to introduce it now, it'd be in 0.72 (which branch has not been cut) via the app template. +If we decide to adopt this proposal, we'll have to broadcast this change to the ecosystem. +Ideally, we'll start by adding the `reactNativeManifest` section to the app template and to `create-react-native-library`. + +We foresee a phase in which only a part of the userbase has this section configured and tool developers need to account for the absence of this section. + +Once the v1.0.0 version of the shape is finalised, we can start integrating it across the board in various tools mentioned in [Proposed Tooling](#proposed-tooling) section. This first wave of support would have to be aligned around a given React Native release, say in 0.73 (which branch has not been cut). + +After adding this first wave of support, and related documentation, we could more broadly communicate to the community and the maintainers how adding this section would benefit them too. -After adding this first wave of support, and related documentation, we could more broadly communicate to the community and the maintainers how adding this section would benefit them too. Especially for library maintainers we'll have another RFC coming up soon to describe how we are planning to better communicate this. +### Documentation update needed -## How we teach this +If we agree on this proposal, we'll have to: -- add to core's documentation (which should happen anyway if we start using it for flags for new arch configuration) -- add to the templates (both app and library) -- have RNDirectory and align-deps mention a call to action to introduce this +- Update the `reactnative.dev` website to mention this (and harmonize the current New Architecture and Hermes configuration pages) +- Update the app/library templates as mentioned +- Potentially add a CTA banner to React Native Directory and `align-deps` to introduce this field. -## Unresolved questions +## Open questions -v1.0.0 of the shape needs to be finalized. +* `v1.0.0` of the schema needs to be finalized and agreed by stakeholders. From fe3e609f94490d9ac50213f5a16f71764c675d8a Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 6 Apr 2023 14:43:42 +0200 Subject: [PATCH 08/25] Clarify version schema --- proposals/0012-introduce-reactNativeMetadata.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/proposals/0012-introduce-reactNativeMetadata.md b/proposals/0012-introduce-reactNativeMetadata.md index 36660312..aba23310 100644 --- a/proposals/0012-introduce-reactNativeMetadata.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -227,6 +227,8 @@ The React Native direcotry can easily consume the `reactNativeManifest` by: ## Future proof and extensibility +We're versioning the schema using the `schema` key in the `reactNativeManifest` section. This allows us to evolve the schema in the future without breaking changes. We believe that the `schema` key should be a semver version string. + We believe that the `reactNativeManifest` section should be extensible as we believe new capabilities and features can be added there. Specifically, we envision to evolve `reactNativeManifest` or `reactNativeManifest.capabilites` as follows: @@ -246,7 +248,7 @@ For an app the section will look as follows: "name": "my-awesome-app", "version": "1.2.3", "reactNativeManifest": { - "version": "1.0", + "schema": "1.0.0", "type": "app", "capabilities": { "hermes": { @@ -275,7 +277,7 @@ For a library the section will look as follows: "name": "my-awesome-library", "version": "1.2.3", "reactNativeManifest": { - "version": "1.0", + "schema": "1.0.0", "type": "library", "capabilities": { "hermes": { From 76cb7db062beb14af3a610a87bce49785f51daa2 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 6 Apr 2023 14:48:22 +0200 Subject: [PATCH 09/25] Add a build logic algoritm section --- proposals/0012-introduce-reactNativeMetadata.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/proposals/0012-introduce-reactNativeMetadata.md b/proposals/0012-introduce-reactNativeMetadata.md index aba23310..d21ec628 100644 --- a/proposals/0012-introduce-reactNativeMetadata.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -207,6 +207,17 @@ Selectively toggling Fabric/TurboModule is a more advanced feature. We believe w This section lists the tooling we believe could be built on top of `reactNativeManifest` to improve the developer experience. +### Build Logic support + +Build tools such as Gradle/CocoaPods or others should account for the `reactNativeManifest` section and use it to configure the build pipeline accordingly to this and future RFCs. Specifically they should follow this logic for a generic capability `foo`. + +1. If the capability `foo` is specificed in the `reactNativeManifest.capabilities` section, use the value specified in the `reactNativeManifest.capabilities.foo` section. + 1. If the user is **also** specifying the capability `foo` in the build tool specific configuration (e.g. inside `gradle.properties` for Android) behave as follows: + 1. If the two values are **compatible**, use them without notifying the user. + 1. If the two values are **incompatible**, notify the user and use the value specified in the `reactNativeManifest.capabilities.foo` section will prevail and the value specified in the build tool specific configuration will be ignored. + 1. If the user is **not** specifying the capability `foo` in the build tool specific configuration, honor the value specified in the `reactNativeManifest.capabilities.foo` section. +1. If the capability `foo` is **not** specificed in the `reactNativeManifest.capabilities` section, honor the value specified in the build tool specific configuration or the default value if not specified. + ### `align-deps` support This RFC originated from a [conversation between Meta and Microsoft](https://github.com/microsoft/rnx-kit/issues/1863) to use `align-deps` as key feature in the New Architecture rollout support, in helping developers understanding if a library they're using is New Architecture compatible or not. Very quickly it became clear that there is currently no straightforward way to know if a library indeed has that support. From 68c906f36cef9af41a12172bc1c944900fb64f3b Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 6 Apr 2023 14:49:41 +0200 Subject: [PATCH 10/25] Add clarification on jsonschema --- proposals/0012-introduce-reactNativeMetadata.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0012-introduce-reactNativeMetadata.md b/proposals/0012-introduce-reactNativeMetadata.md index d21ec628..39f37b0b 100644 --- a/proposals/0012-introduce-reactNativeMetadata.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -227,6 +227,7 @@ Therefore we believe `align-deps` can benefit from this information and be exten ### React Native CLI support Ideally the CLI (or any other build tool) should be able to consume the `reactNativeManifest` section and warn at build time if a configuration is invalid (either by having an invalid value or by having a mixture of invalid configurations). +For example, the CLI could use [jsonschema](https://npmjs.com/package/jsonschema) at build time and validate the `package.json` against the schema we provide. It's outside the scope of this RFC to delve into details of which tool will implement which logic and we defer this to a future discussion. From 3b38eb4ea019c8470e667833aaebae8e5f075849 Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Thu, 6 Jul 2023 15:11:44 +0200 Subject: [PATCH 11/25] Fix typos --- .../0012-introduce-reactNativeMetadata.md | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/proposals/0012-introduce-reactNativeMetadata.md b/proposals/0012-introduce-reactNativeMetadata.md index 39f37b0b..7ab00cf8 100644 --- a/proposals/0012-introduce-reactNativeMetadata.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -1,8 +1,8 @@ --- -title: [DRAFT] Introducing `reactNativeManifest` to `package.json` for React Native specific metadata +title: DRAFT | Introducing `reactNativeManifest` to `package.json` for React Native specific metadata author: - - Lorenzo Sciandra - - Nicola Corti +- Lorenzo Sciandra +- Nicola Corti date: 25-01-2021 --- @@ -27,24 +27,24 @@ As of today, there is no simple programmatic way to know if a NPM package is a R Those indicators are all non-exhaustive. Similarly, there is no easy way to understand if a library is exposing a native module or not. -The React Native CLI is facing this difficulty and tries to circumvent it by inspecting the native code and search for subclasses of `*ReactPackage` using complex regular expressions ([example](https://github.com/react-native-community/cli/blob/605c542d18efcb02f217d3c85726fa73a79054c2/packages/cli-platform-android/src/config/findPackageClassName.ts#L35)) due to the lack of a declarative way to express this information. +The React Native CLI is facing this difficulty and tries to circumvent it by inspecting the native code and searching for subclasses of `*ReactPackage` using complex regular expressions ([example](https://github.com/react-native-community/cli/blob/605c542d18efcb02f217d3c85726fa73a79054c2/packages/cli-platform-android/src/config/findPackageClassName.ts#L35)) due to the lack of a declarative way to express this information. -Another practical examples of this need are the [React Native Directory](https://reactnative.directory/) attempting to [programmatically infer](https://github.com/react-native-community/directory/pull/870) New Architecture support from the presence of the `codegenConfig` fields in the `package.json` of a library. The `codegenConfig` filed is not intended to carry semantic information about New Architecture support, and is definitely a non-exhaustive solution for this problem. +Another practical example of this need is the [React Native Directory](https://reactnative.directory/) attempting to [programmatically infer](https://github.com/react-native-community/directory/pull/870) the New Architecture support from the presence of the `codegenConfig` fields in the `package.json` of a library. The `codegenConfig` field is not intended to carry semantic information about New Architecture support, and is definitely a non-exhaustive solution for this problem. -While we can continue to build tooling that tries to infer metadata and capabilities of a library, providing a declarative way to declared such capabilites provides a series of benefit: +While we can continue to build tooling that tries to infer metadata and capabilities of a library, providing a declarative way to describe such capabilities yields a series of benefit: * **predictability**: developers can easily understand what a library is capable of doing and if such capability matches their app. * **tooling**: tooling can easily consume this information and provide a better experience to developers. * **single source of thruth**: a single manifest will act as a source of truth for all those capabilities and flag definition, that can easily be consumed by different platforms to provide a unified way to enable/disable capabilities. -* **verifiability**: the manifest can be easily verified & linted by tooling to ensure that app & library are not using an incompatible set of compatibilites. Similarly, the JSON schema we publish can be used to validate the `reactNativeManifest` section. +* **verifiability**: the manifest can be easily verified and linted by tooling to ensure that apps and libraries are not using an incompatible set of dependencies. Similarly, the JSON schema we publish can be used to validate the `reactNativeManifest` section. ## Use Cases -This section lists the use case we envision for the `reactNativeManifest` section. +This section lists the use cases we envision for the `reactNativeManifest` section. ### Declaring the type of a project For the sake of this RFC, we consider a React Native project as either an app or a library. -While an app project can contain native modules and components, it's **not** possible for a app to "consume" another app as a library as this would break the native build system invariants (i.e. a single `AndroidManifest.xml` and `Info.plist` file per app, etc.). If an app projects wishes to distribute some of its native modules or components, they need to be extracted into a library project. +While an app project can contain native modules and components, it's **not** possible for an app to "consume" another app as a library. This would break the native build system invariants (i.e. a single `AndroidManifest.xml` and `Info.plist` file per app, etc.). If an app projects wishes to distribute some of its native modules or components, they need to be extracted into a library project. We're proposing to declare if a project is either a library or an app by adding the `type` field (as `"app"` or `"library"`) to the `reactNativeManifest` section of the `package.json` file. @@ -59,19 +59,19 @@ We're proposing to declare if a project is either a library or an app by adding ### New Architecture support One of the primary driver of this proposal is the [New React Native Architecture support](https://reactnative.dev/docs/the-new-architecture/landing-page). Specifically we believe that the ecosystem is currently affected by: -* Lack of a declarative way to define if a library is compatible with New Architecture - * As described in the [rationale](#rationale), tooling needs to infer this information from the presence of the `codegenConfig` field in the `package.json` of a library. Libraries might decide to don't use codegen and still be compatible with New Architecture, so this method is not exhaustive. - * The alternative at this stage is to inspect the code and check the API callsites if they are New Architecture or Old Architecture compatible. -* Lack of a declarative way to enable New Architecture on both platforms - * Currently New Architecture is enabled by a Gradle Property `newArchEnabled` on Android in the `gradle.properties` file and by invokign `RCT_NEW_ARCH_ENABLED pod install` on iOS. - * As of today, there is no way to enable New Architecture for an app project for both platforms - * Moreover, the file vs environment variable leads to a scenario where you can't know if a app supports New Architecture or not by inspecting the code (as New Architecture support is known at `pod install` time and is not codified in the codebase) +* Lack of a declarative way to define if a library is compatible with the New Architecture + * As described in the [rationale](#rationale), tooling needs to infer this information from the presence of the `codegenConfig` field in the `package.json` of a library. Libraries might decide not to use Codegen and still be compatible with the New Architecture, so this method is not exhaustive. + * The alternative at this stage is to inspect the code and check whether the API callsites are New Architecture or Old Architecture compatible. +* Lack of a declarative way to enable the New Architecture on both platforms + * Currently the New Architecture is enabled by a Gradle Property `newArchEnabled` on Android in the `gradle.properties` file and by invoking `RCT_NEW_ARCH_ENABLED=1 bundle exec pod install` on iOS. + * As of today, there is no way to enable the New Architecture for an app project for both platforms + * Moreover, the difference in how the New Architecture is enabled leads to a scenario where you can't statically know if an app supports New Architecture or not (as the New Architecture support is known at `pod install` time and is not codified in the codebase) Therefore we propose to add the `newArch` section to the `reactNativeManifest.capabilities` of **both apps and libraries** with the following semantic: -* For Apps: `newArch.enabled==true` implies that the app wants to use the New Architecture. -* For Libraries: `newArch.enabled==true` implies that the library is compatible with the New Architecture. +* For Apps: `newArch.enabled==true` means that the app wants to use the New Architecture. +* For Libraries: `newArch.enabled==true` means that the library is compatible with the New Architecture. -Tools can be built on top of this information to check that a app with `newArch.enabled==true` is not accepting libraries with `newArch.enabled==false` and warning against library that don't have the key specified. +Tools can be built on top of this information to check that an app with `newArch.enabled==true` is not accepting libraries with `newArch.enabled==false` and to warn against library that don't have the key specified. The setup would look as follows for both apps and libraries: @@ -87,7 +87,7 @@ The setup would look as follows for both apps and libraries: } ``` -This section will allows also for split configuration between platforms: +This section will allow also for split configuration between platforms: ```json { @@ -112,7 +112,7 @@ This section will allows also for split configuration between platforms: #### `codegenConfig` support -Similarly to New Architecture support metadata, the `codegenConfig` is a key metadata of the New Architecture build pipeline. +Similarly to the New Architecture support metadata, the `codegenConfig` is a key metadata of the New Architecture build pipeline. Currently the `codegenConfig` is a **top-level** key in the `package.json` of a project. We propose to move this key under the `reactNativeManifest.capabilites` section as follows: @@ -135,7 +135,7 @@ We propose to move this key under the `reactNativeManifest.capabilites` section ### Hermes Support -Similarly to New Architecture support, the current way to enable/disable the Hermes engine is toggled by using a Gradle Property `hermesEnabled` on Android and changing the `:hermes_enabled` property in the Podfile. +Similarly to the New Architecture support, the current way to enable/disable the Hermes engine is toggled by using a Gradle Property `hermesEnabled` on Android and changing the `:hermes_enabled` property in the Podfile. We propose to add the `hermes` section under the `reactNativeManifest.capabilities` section as follows: @@ -200,8 +200,8 @@ For the time being, we're not planning to add a `version` section to the `reactN ### TurboModule/Fabric toggles -At the time of writing, we prefer to don't offer a dedicated section to toggle Fabric/TurboModule capability inside `reactNativeManifest`. The rationale is that we believe that the `newArch` section is sufficient to expose the New Architecture to users. -Selectively toggling Fabric/TurboModule is a more advanced feature. We believe we'll still be offering a more advanced way to feature toggle such infrastructure pieces, but at the current state, our preference is to not expose such capability in the top level `reactNativeManifest` section. +At the time of writing, we prefer not to offer a dedicated section to toggle Fabric/TurboModule capability inside `reactNativeManifest`. The rationale is that we believe that the `newArch` section is sufficient to expose the New Architecture to users. +Selectively toggling Fabric/TurboModule is a more advanced feature. We believe we'll still be offering a more advanced way to enable or disable those pieces of the New Architecture infrastructure, but at the current state, our preference is not to expose such capability in the top level `reactNativeManifest` section. ## Proposed Tooling @@ -220,9 +220,9 @@ Build tools such as Gradle/CocoaPods or others should account for the `reactNati ### `align-deps` support -This RFC originated from a [conversation between Meta and Microsoft](https://github.com/microsoft/rnx-kit/issues/1863) to use `align-deps` as key feature in the New Architecture rollout support, in helping developers understanding if a library they're using is New Architecture compatible or not. Very quickly it became clear that there is currently no straightforward way to know if a library indeed has that support. +This RFC originated from a [conversation between Meta and Microsoft](https://github.com/microsoft/rnx-kit/issues/1863) to use `align-deps` as key feature in the New Architecture rollout support, in helping developers understand if a library they're using is compatible with the New Architecture or not. Very quickly it became clear that there is currently no straightforward way to know if a library indeed has that support. -Therefore we believe `align-deps` can benefit from this information and be extended to offer New Architecture support information. +Therefore we believe `align-deps` can benefit from this information and can be extended to offer New Architecture support information. ### React Native CLI support @@ -247,7 +247,7 @@ Specifically, we envision to evolve `reactNativeManifest` or `reactNativeManifes 1. Each addition of a new capability [requires a new RFC](https://github.com/react-native-community/discussions-and-proposals#proposals) to be approved. 2. Each RFC should define a new section in the `reactNativeManifest` with what's the intended use case. 3. Each RFC should define what are the sensible defaults and should refrain from breaking changes. -4. Each RFC should define the deprecation strategy for previous flags/features related to the same capability. +4. Each RFC should define the deprecation strategy for previous flags/features related to the same capability, if needed. ## Complete Example @@ -324,7 +324,7 @@ The JSON Schema will be published as soon as we find an agreement on the key nam If we decide to adopt this proposal, we'll have to broadcast this change to the ecosystem. Ideally, we'll start by adding the `reactNativeManifest` section to the app template and to `create-react-native-library`. -We foresee a phase in which only a part of the userbase has this section configured and tool developers need to account for the absence of this section. +We foresee a phase in which only a part of the userbase has this section configured, and tool and developers need to account for the absence of this section. Once the v1.0.0 version of the shape is finalised, we can start integrating it across the board in various tools mentioned in [Proposed Tooling](#proposed-tooling) section. This first wave of support would have to be aligned around a given React Native release, say in 0.73 (which branch has not been cut). From d60222b2304575e2be83859a7db1079b267a822d Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Wed, 25 Jan 2023 16:49:22 +0000 Subject: [PATCH 12/25] add RFC for reactNativeMeta --- proposals/0011-introduce-reactNativeMeta.md | 148 ++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 proposals/0011-introduce-reactNativeMeta.md diff --git a/proposals/0011-introduce-reactNativeMeta.md b/proposals/0011-introduce-reactNativeMeta.md new file mode 100644 index 00000000..a7bb4f63 --- /dev/null +++ b/proposals/0011-introduce-reactNativeMeta.md @@ -0,0 +1,148 @@ +--- +title: Introducing `reactNativeMeta` to `package.json`, for RN specific metadata +author: + - Lorenzo Sciandra +date: 25-01-2021 +--- + +# RFC0011: Introducing `reactNativeMeta` to `package.json`, for RN specific metadata + +## Summary + +This RFC wants to introduce and formally discuss a new section for the package.json of a react-native project (app or library), called `reactNativeMeta`. + +This new section will allow developer to express certain characteristics of their code in a more formalized manner, that can easily used by tooling to act towards the code accordingly. + +**Note well**: this is but the first draft of the actual shape and fields. The goal of this draft is to collect feedback and help finalize the spec for this section. + +## Basic examples + +This is how this section would look like... + +...for a library: + +```json +{ + "name": "@rnx-kit/metro-serializer", + "version": "1.0.11", + ..., + "reactNativeMeta": { + "version": "2.3", + "type": "library", + "features": { + "fabric": "true", + "android": { + "turbomodules": "true", + }, + }, + "requirements": { + "react-native": ">= 0.71.0", + "expo": ">= 46.0.0", + "expo-modules-core": "1.2.0" + } + ... +} +``` + +...for an app: + +```json +{ + "name": "contoso", + "version": "2.0.3", + ..., + "reactNativeMeta": { + "version": "1.0", + "type": "app", + "features": { + "newArchEnabled": "true", + "android": { + "codegen": "true", + "turbomodules": "true", + }, + // Future example + // "metroExportsMode": "strict" + }, + ... + } +``` + +## Motivation + +The conversation that originated this idea was [an ask from the Meta team](https://github.com/microsoft/rnx-kit/issues/1863) to have `align-deps` help developers in the community migrating to the new architecture by providing insights into which libraries would have Fabric/TM support. Very quickly it became clear that there is currently no straightforward way to know if a library indeed has that support. + +A similar problem was also encountered by [React Native Directory](https://reactnative.directory/), that tried to [programmatically infer](https://github.com/react-native-community/directory/pull/870) support via the presence of certain fields in the package.json of a library - this because one can get access to the manifest file via npm without having to download the entire node_module. + +It became clear that there was a need to be able to communicate and parse certain information about a react-native library via its `package.json`, which led to [a meeting](https://github.com/microsoft/rnx-kit/discussions/2125) between Meta, Microsoft and Expo to figure out a common solution. + +This RFC is the result of that meeting: a new section of the `package.json`, called `reactNativeMeta`, to contain certain metadata about a react-native project - it being either a library or an app. + +There are a few areas that this section could express in a consistent manner; here's a list of the ones we could think of (in no particular order): + +- if a library supports the new architecture, turbo modules or fabric or both +- which versions of react-native a library supports in a given release +- if an app has opted in into Fabric, TM, or both +- if an app is using Hermes or another js engine + +Sidenote: the name `reactNativeMeta` was chosen to mirror the camel case pattern used by other fields such as `peerDependenciesMeta`. + +## Detailed design + +This is the section that in this first draft will need to be properly finalised. For now, here's what we envisioned: + +```js +reactNativeMeta: { + version: string, // ex. 1.0.0 - we know that this spec might evolve in the future + type: string; // "app" or "library" + features: { + // can be global... + newArchEnabled: boolean, + hermesEnabled: boolean, + // ...or platform specific + android: { + newArchEnabled: boolean, + fabric: boolean, + turbomodules: boolean, + hermesEnabled: boolean, + }, + ios: { + ... // same as android + }, + macos: { + ... // same as android + }, + windows: { + ... // same as android + }, + // more platforms, params - add a comment here + }, + // this section would most likely only be relevant for libraries + requirements: { + react-native: string, // semver compliant range of versions of RN supported + expo: string, // semver compliant range of versions of Expo supported (if any) + } +} +``` + +## Drawbacks + +The main drawback is that we'd need to convince developers to start using it, so we'd have to expect a phase in which only a part of the userbase has this section configured and tool developers need to account for the section missing entirely. + +## Alternatives + +This section is, in itself, fully original and new: there are other files that are used to define configurations for various aspect of react-native based projects (such as `react-native.config.js`, `app.json`, Expo's `app.config.js`, `expo-module.config.json`) but by design this section does **not** overlap with any of them. Explicitly, this one has the purpose of being filled only with metadata to be read by package managers and other tools. + +## Adoption strategy + +Once the v1.0.0 version of the shape is finalised, we can start integrating it across the board in tools such has `align-deps` and the RNDirectory, and in the react-native tools themselves. This first wave of support would have to be coordinated around a certain release that lives in RN's main branch, such as now it'd be introduced in 0.72 (which branch has not been cut). + +After adding this first wave of support, and related documentation, we could more broadly communicate to the community and the maintainers how adding this section would benefit them too. Especially for library maintainers we'll have another RFC coming up soon to describe how we are planning to better communicate this. + +## How we teach this + +- add to core's documentation (which should happen anyway if we start using it for flags for new arch configuration) +- have RNDirectory and align-deps mention a call to action to introduce this + +## Unresolved questions + +v1.0.0 of the shape needs to be finalized. From b67e94fb6cd65fdef23d1f115a65ce5db7d76bde Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Wed, 25 Jan 2023 16:56:33 +0000 Subject: [PATCH 13/25] quick cleanup --- proposals/0011-introduce-reactNativeMeta.md | 66 ++++++++++----------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/proposals/0011-introduce-reactNativeMeta.md b/proposals/0011-introduce-reactNativeMeta.md index a7bb4f63..8e8e0c2e 100644 --- a/proposals/0011-introduce-reactNativeMeta.md +++ b/proposals/0011-introduce-reactNativeMeta.md @@ -23,24 +23,24 @@ This is how this section would look like... ```json { - "name": "@rnx-kit/metro-serializer", - "version": "1.0.11", - ..., - "reactNativeMeta": { - "version": "2.3", - "type": "library", - "features": { - "fabric": "true", + "name": "@rnx-kit/metro-serializer", + "version": "1.0.11", + ..., + "reactNativeMeta": { + "version": "2.3", + "type": "library", + "features": { + "fabric": "true", "android": { "turbomodules": "true", - }, - }, - "requirements": { - "react-native": ">= 0.71.0", - "expo": ">= 46.0.0", - "expo-modules-core": "1.2.0" + }, + }, + "requirements": { + "react-native": ">= 0.71.0", + "expo": ">= 46.0.0", + "expo-modules-core": "1.2.0" + } } - ... } ``` @@ -48,23 +48,23 @@ This is how this section would look like... ```json { - "name": "contoso", - "version": "2.0.3", - ..., - "reactNativeMeta": { + "name": "contoso", + "version": "2.0.3", + ..., + "reactNativeMeta": { "version": "1.0", "type": "app", "features": { - "newArchEnabled": "true", - "android": { - "codegen": "true", - "turbomodules": "true", - }, - // Future example - // "metroExportsMode": "strict" + "newArchEnabled": "true", + "android": { + "codegen": "true", + "turbomodules": "true", + }, + // Future example + // "metroExportsMode": "strict" }, ... - } + } ``` ## Motivation @@ -97,18 +97,18 @@ reactNativeMeta: { features: { // can be global... newArchEnabled: boolean, - hermesEnabled: boolean, + jsEngine: string, // ex. "hermes", "jsc", "V8"... // ...or platform specific android: { newArchEnabled: boolean, fabric: boolean, turbomodules: boolean, - hermesEnabled: boolean, + jsEngine: string, }, - ios: { + iOs: { ... // same as android }, - macos: { + macOs: { ... // same as android }, windows: { @@ -118,7 +118,7 @@ reactNativeMeta: { }, // this section would most likely only be relevant for libraries requirements: { - react-native: string, // semver compliant range of versions of RN supported + "react-native": string, // semver compliant range of versions of RN supported expo: string, // semver compliant range of versions of Expo supported (if any) } } @@ -130,7 +130,7 @@ The main drawback is that we'd need to convince developers to start using it, so ## Alternatives -This section is, in itself, fully original and new: there are other files that are used to define configurations for various aspect of react-native based projects (such as `react-native.config.js`, `app.json`, Expo's `app.config.js`, `expo-module.config.json`) but by design this section does **not** overlap with any of them. Explicitly, this one has the purpose of being filled only with metadata to be read by package managers and other tools. +`reactNativeMeta` is, by design, fully original and new: there are other files that are used to define configurations for various aspect of react-native based projects (such as `react-native.config.js`, `app.json`, Expo's `app.config.js`, `expo-module.config.json`) but this one does **not** overlap with any of them. Explicitly, this one has the purpose of being filled only with metadata to be read by package managers and other tools. ## Adoption strategy From d11d5379bf21ff268b7a2029923b5854f2002616 Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Wed, 25 Jan 2023 16:57:59 +0000 Subject: [PATCH 14/25] a bit more cleanup --- ...reactNativeMeta.md => 0012-introduce-reactNativeMeta.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{0011-introduce-reactNativeMeta.md => 0012-introduce-reactNativeMeta.md} (98%) diff --git a/proposals/0011-introduce-reactNativeMeta.md b/proposals/0012-introduce-reactNativeMeta.md similarity index 98% rename from proposals/0011-introduce-reactNativeMeta.md rename to proposals/0012-introduce-reactNativeMeta.md index 8e8e0c2e..9f8277da 100644 --- a/proposals/0011-introduce-reactNativeMeta.md +++ b/proposals/0012-introduce-reactNativeMeta.md @@ -5,7 +5,7 @@ author: date: 25-01-2021 --- -# RFC0011: Introducing `reactNativeMeta` to `package.json`, for RN specific metadata +# RFC0012: Introducing `reactNativeMeta` to `package.json`, for RN specific metadata ## Summary @@ -32,8 +32,8 @@ This is how this section would look like... "features": { "fabric": "true", "android": { - "turbomodules": "true", - }, + "turbomodules": "true", + }, }, "requirements": { "react-native": ">= 0.71.0", From 882052ae46bddc74de77092590fe879f5ec70118 Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Fri, 27 Jan 2023 16:09:03 +0000 Subject: [PATCH 15/25] meta->metadata --- ...d => 0012-introduce-reactNativeMetadata.md} | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) rename proposals/{0012-introduce-reactNativeMeta.md => 0012-introduce-reactNativeMetadata.md} (84%) diff --git a/proposals/0012-introduce-reactNativeMeta.md b/proposals/0012-introduce-reactNativeMetadata.md similarity index 84% rename from proposals/0012-introduce-reactNativeMeta.md rename to proposals/0012-introduce-reactNativeMetadata.md index 9f8277da..482ba6e4 100644 --- a/proposals/0012-introduce-reactNativeMeta.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -1,15 +1,15 @@ --- -title: Introducing `reactNativeMeta` to `package.json`, for RN specific metadata +title: Introducing `reactNativeMetadata` to `package.json`, for RN specific metadata author: - Lorenzo Sciandra date: 25-01-2021 --- -# RFC0012: Introducing `reactNativeMeta` to `package.json`, for RN specific metadata +# RFC0012: Introducing `reactNativeMetadata` to `package.json`, for RN specific metadata ## Summary -This RFC wants to introduce and formally discuss a new section for the package.json of a react-native project (app or library), called `reactNativeMeta`. +This RFC wants to introduce and formally discuss a new section for the package.json of a react-native project (app or library), called `reactNativeMetadata`. This new section will allow developer to express certain characteristics of their code in a more formalized manner, that can easily used by tooling to act towards the code accordingly. @@ -26,7 +26,7 @@ This is how this section would look like... "name": "@rnx-kit/metro-serializer", "version": "1.0.11", ..., - "reactNativeMeta": { + "reactNativeMetadata": { "version": "2.3", "type": "library", "features": { @@ -51,7 +51,7 @@ This is how this section would look like... "name": "contoso", "version": "2.0.3", ..., - "reactNativeMeta": { + "reactNativeMetadata": { "version": "1.0", "type": "app", "features": { @@ -75,7 +75,7 @@ A similar problem was also encountered by [React Native Directory](https://react It became clear that there was a need to be able to communicate and parse certain information about a react-native library via its `package.json`, which led to [a meeting](https://github.com/microsoft/rnx-kit/discussions/2125) between Meta, Microsoft and Expo to figure out a common solution. -This RFC is the result of that meeting: a new section of the `package.json`, called `reactNativeMeta`, to contain certain metadata about a react-native project - it being either a library or an app. +This RFC is the result of that meeting: a new section of the `package.json`, called `reactNativeMetadata`, to contain certain metadata about a react-native project - it being either a library or an app. There are a few areas that this section could express in a consistent manner; here's a list of the ones we could think of (in no particular order): @@ -84,14 +84,14 @@ There are a few areas that this section could express in a consistent manner; he - if an app has opted in into Fabric, TM, or both - if an app is using Hermes or another js engine -Sidenote: the name `reactNativeMeta` was chosen to mirror the camel case pattern used by other fields such as `peerDependenciesMeta`. +Sidenote: the name `reactNativeMetadata` was chosen to mirror the camel case pattern used by other fields such as `peerDependenciesMeta`. ## Detailed design This is the section that in this first draft will need to be properly finalised. For now, here's what we envisioned: ```js -reactNativeMeta: { +reactNativeMetadata: { version: string, // ex. 1.0.0 - we know that this spec might evolve in the future type: string; // "app" or "library" features: { @@ -130,7 +130,7 @@ The main drawback is that we'd need to convince developers to start using it, so ## Alternatives -`reactNativeMeta` is, by design, fully original and new: there are other files that are used to define configurations for various aspect of react-native based projects (such as `react-native.config.js`, `app.json`, Expo's `app.config.js`, `expo-module.config.json`) but this one does **not** overlap with any of them. Explicitly, this one has the purpose of being filled only with metadata to be read by package managers and other tools. +`reactNativeMetadata` is, by design, fully original and new: there are other files that are used to define configurations for various aspect of react-native based projects (such as `react-native.config.js`, `app.json`, Expo's `app.config.js`, `expo-module.config.json`) but this one does **not** overlap with any of them. Explicitly, this one has the purpose of being filled only with metadata to be read by package managers and other tools. ## Adoption strategy From cb9ac9d22cb1514a7b8ad4788c37eee434dd9e66 Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Fri, 27 Jan 2023 16:11:36 +0000 Subject: [PATCH 16/25] capitalization --- proposals/0012-introduce-reactNativeMetadata.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0012-introduce-reactNativeMetadata.md b/proposals/0012-introduce-reactNativeMetadata.md index 482ba6e4..ebbc0818 100644 --- a/proposals/0012-introduce-reactNativeMetadata.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -105,10 +105,10 @@ reactNativeMetadata: { turbomodules: boolean, jsEngine: string, }, - iOs: { + ios: { ... // same as android }, - macOs: { + macos: { ... // same as android }, windows: { From 5be7665773e48d74682a8a4d4496752f3aba8b9e Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Fri, 27 Jan 2023 16:13:59 +0000 Subject: [PATCH 17/25] a bit more details --- proposals/0012-introduce-reactNativeMetadata.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0012-introduce-reactNativeMetadata.md b/proposals/0012-introduce-reactNativeMetadata.md index ebbc0818..07640c6e 100644 --- a/proposals/0012-introduce-reactNativeMetadata.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -134,13 +134,14 @@ The main drawback is that we'd need to convince developers to start using it, so ## Adoption strategy -Once the v1.0.0 version of the shape is finalised, we can start integrating it across the board in tools such has `align-deps` and the RNDirectory, and in the react-native tools themselves. This first wave of support would have to be coordinated around a certain release that lives in RN's main branch, such as now it'd be introduced in 0.72 (which branch has not been cut). +Once the v1.0.0 version of the shape is finalised, we can start integrating it across the board in tools such has `align-deps` and the RNDirectory, and in the react-native tools themselves. This first wave of support would have to be coordinated around a certain release that lives in RN's main branch; for example, if we were to introduce it now, it'd be in 0.72 (which branch has not been cut) via the app template. After adding this first wave of support, and related documentation, we could more broadly communicate to the community and the maintainers how adding this section would benefit them too. Especially for library maintainers we'll have another RFC coming up soon to describe how we are planning to better communicate this. ## How we teach this - add to core's documentation (which should happen anyway if we start using it for flags for new arch configuration) +- add to the templates (both app and library) - have RNDirectory and align-deps mention a call to action to introduce this ## Unresolved questions From 62ef34b13c5afeaca825d70f4f31dd6309729b57 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Fri, 17 Mar 2023 19:13:02 +0000 Subject: [PATCH 18/25] Reword the rfc by @cortinico to reduce scope --- .../0012-introduce-reactNativeMetadata.md | 384 +++++++++++++----- 1 file changed, 282 insertions(+), 102 deletions(-) diff --git a/proposals/0012-introduce-reactNativeMetadata.md b/proposals/0012-introduce-reactNativeMetadata.md index 07640c6e..36660312 100644 --- a/proposals/0012-introduce-reactNativeMetadata.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -1,149 +1,329 @@ --- -title: Introducing `reactNativeMetadata` to `package.json`, for RN specific metadata +title: [DRAFT] Introducing `reactNativeManifest` to `package.json` for React Native specific metadata author: - Lorenzo Sciandra + - Nicola Corti date: 25-01-2021 --- -# RFC0012: Introducing `reactNativeMetadata` to `package.json`, for RN specific metadata +# RFC0588: [DRAFT] Introducing `reactNativeManifest` to `package.json` for React Native specific metadata ## Summary -This RFC wants to introduce and formally discuss a new section for the package.json of a react-native project (app or library), called `reactNativeMetadata`. +> **Warning** ⚠️ This is the initial draft of this proposal. The goal of this draft is to collect feedback and help finalize the spec for this section. -This new section will allow developer to express certain characteristics of their code in a more formalized manner, that can easily used by tooling to act towards the code accordingly. +This RFC introduces and formally discuss the concept of _React Native Manifest_, a set of metadata about a React Native project (either app or library). This metadata can be consumed by tooling to enhance the developer experience and support the React Native ecosystem. -**Note well**: this is but the first draft of the actual shape and fields. The goal of this draft is to collect feedback and help finalize the spec for this section. +Practically, this RFC proposes to add a `reactNativeManifest` section to the `package.json` file of a React Native app or library. +This new section will allow developers to follow a declarative approach to express capabilities of apps and libraries in a more formalized manner. -## Basic examples +## Rationale -This is how this section would look like... +The need to specify **capabilities and metadata** for a react-native library is inherent to the React Native ecosystem. -...for a library: +As of today, there is no simple programmatic way to know if a NPM package is a React Native library. Good indicators could be: +* Presence of a `react-native:*` dependency in the `peerDependencies` section of the `package.json` ([example](https://github.com/RonRadtke/react-native-blob-util/blob/80628d418a5c81439dc2c1a1f05fae6406f0ba7f/package.json#L46C10-L49)) +* Presence of React Native specific files such as `react-native.config.js` files +Those indicators are all non-exhaustive. + +Similarly, there is no easy way to understand if a library is exposing a native module or not. +The React Native CLI is facing this difficulty and tries to circumvent it by inspecting the native code and search for subclasses of `*ReactPackage` using complex regular expressions ([example](https://github.com/react-native-community/cli/blob/605c542d18efcb02f217d3c85726fa73a79054c2/packages/cli-platform-android/src/config/findPackageClassName.ts#L35)) due to the lack of a declarative way to express this information. + +Another practical examples of this need are the [React Native Directory](https://reactnative.directory/) attempting to [programmatically infer](https://github.com/react-native-community/directory/pull/870) New Architecture support from the presence of the `codegenConfig` fields in the `package.json` of a library. The `codegenConfig` filed is not intended to carry semantic information about New Architecture support, and is definitely a non-exhaustive solution for this problem. + +While we can continue to build tooling that tries to infer metadata and capabilities of a library, providing a declarative way to declared such capabilites provides a series of benefit: +* **predictability**: developers can easily understand what a library is capable of doing and if such capability matches their app. +* **tooling**: tooling can easily consume this information and provide a better experience to developers. +* **single source of thruth**: a single manifest will act as a source of truth for all those capabilities and flag definition, that can easily be consumed by different platforms to provide a unified way to enable/disable capabilities. +* **verifiability**: the manifest can be easily verified & linted by tooling to ensure that app & library are not using an incompatible set of compatibilites. Similarly, the JSON schema we publish can be used to validate the `reactNativeManifest` section. + +## Use Cases + +This section lists the use case we envision for the `reactNativeManifest` section. + +### Declaring the type of a project + +For the sake of this RFC, we consider a React Native project as either an app or a library. +While an app project can contain native modules and components, it's **not** possible for a app to "consume" another app as a library as this would break the native build system invariants (i.e. a single `AndroidManifest.xml` and `Info.plist` file per app, etc.). If an app projects wishes to distribute some of its native modules or components, they need to be extracted into a library project. + +We're proposing to declare if a project is either a library or an app by adding the `type` field (as `"app"` or `"library"`) to the `reactNativeManifest` section of the `package.json` file. + +```json +{ + "reactNativeManifest": { + "type": "library" + } +} +``` + +### New Architecture support + +One of the primary driver of this proposal is the [New React Native Architecture support](https://reactnative.dev/docs/the-new-architecture/landing-page). Specifically we believe that the ecosystem is currently affected by: +* Lack of a declarative way to define if a library is compatible with New Architecture + * As described in the [rationale](#rationale), tooling needs to infer this information from the presence of the `codegenConfig` field in the `package.json` of a library. Libraries might decide to don't use codegen and still be compatible with New Architecture, so this method is not exhaustive. + * The alternative at this stage is to inspect the code and check the API callsites if they are New Architecture or Old Architecture compatible. +* Lack of a declarative way to enable New Architecture on both platforms + * Currently New Architecture is enabled by a Gradle Property `newArchEnabled` on Android in the `gradle.properties` file and by invokign `RCT_NEW_ARCH_ENABLED pod install` on iOS. + * As of today, there is no way to enable New Architecture for an app project for both platforms + * Moreover, the file vs environment variable leads to a scenario where you can't know if a app supports New Architecture or not by inspecting the code (as New Architecture support is known at `pod install` time and is not codified in the codebase) + +Therefore we propose to add the `newArch` section to the `reactNativeManifest.capabilities` of **both apps and libraries** with the following semantic: +* For Apps: `newArch.enabled==true` implies that the app wants to use the New Architecture. +* For Libraries: `newArch.enabled==true` implies that the library is compatible with the New Architecture. + +Tools can be built on top of this information to check that a app with `newArch.enabled==true` is not accepting libraries with `newArch.enabled==false` and warning against library that don't have the key specified. + +The setup would look as follows for both apps and libraries: + +```json +{ + "reactNativeManifest": { + "capabilities": { + "newArch": { + "enabled": true + } + } + } +} +``` + +This section will allows also for split configuration between platforms: + +```json +{ + "reactNativeManifest": { + "android": { + "capabilities": { + "newArch": { + "enabled": true + } + } + }, + "ios": { + "capabilities": { + "newArch": { + "enabled": true + } + } + } + } +} +``` + +#### `codegenConfig` support + +Similarly to New Architecture support metadata, the `codegenConfig` is a key metadata of the New Architecture build pipeline. +Currently the `codegenConfig` is a **top-level** key in the `package.json` of a project. + +We propose to move this key under the `reactNativeManifest.capabilites` section as follows: +```json +{ + "reactNativeManifest": { + "capabilities": { + "codegenConfig": { + "name": "MyLib", + "type": "all", + "jsSrcsDir": ".", + "android": { + "javaPackageName": "com.example.mypackage" + } + } + } + } +} +``` + +### Hermes Support + +Similarly to New Architecture support, the current way to enable/disable the Hermes engine is toggled by using a Gradle Property `hermesEnabled` on Android and changing the `:hermes_enabled` property in the Podfile. + +We propose to add the `hermes` section under the `reactNativeManifest.capabilities` section as follows: ```json { - "name": "@rnx-kit/metro-serializer", - "version": "1.0.11", - ..., - "reactNativeMetadata": { - "version": "2.3", - "type": "library", - "features": { - "fabric": "true", - "android": { - "turbomodules": "true", - }, - }, - "requirements": { - "react-native": ">= 0.71.0", - "expo": ">= 46.0.0", - "expo-modules-core": "1.2.0" + "reactNativeManifest": { + "capabilities": { + "hermes": { + "enabled": true + } + } + } +} +``` + +which will also allow for split configuration between platforms as follows: + +```json +{ + "reactNativeManifest": { + "android": { + "capabilities": { + "hermes": { + "enabled": true + } + } + }, + "ios": { + "capabilities": { + "hermes": { + "enabled": true } + } } + } } ``` -...for an app: +### React Native Release feature flagging + +We currently have a numer of different files where capabilities of React Native can be toggled to enable/disable features in the runtime. +For example we have: +* [android/gradle.properties](https://github.com/facebook/react-native/blob/main/packages/react-native/template/android/gradle.properties) to specify key-value build time properties for Android +* [android/app/build.gradle](https://github.com/facebook/react-native/blob/main/packages/react-native/template/android/app/build.gradle) to specify build time configuration for Android +* [ReactNativeFeatureFlags.js](https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/ReactNative/ReactNativeFeatureFlags.js) which contains runtime feature toggles for the Javascript layer. +* [ReactFeatureFlags.java](https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java) which contains runtime feature toggles for Android. +* [ReactNativeConfig.cpp](https://github.com/facebook/react-native/blob/main/packages/react-native/ReactCommon/react/config/ReactNativeConfig.cpp) which contains runtime feature toggles for the C++ layer. + +Most of those config flags are undocumented and they're not verified for invalid configuration. Specifically, some feature flags require to be toggled on multiple layers, and it's easy to forget to toggle one of them. Invalid combinations of those flags could lead to unexpected runtime behavior. + +We propose to define `reactNativeManifest` as the **preferred entry point** for all the user facing entry points. All the tooling should be adapted to consume the information from the `reactNativeManifest` section and have sensible defaults defined by the tooling itself. + +It's outside the scope of this RFC to declare all the new sections we intend to add, and we defer to the [Future proof and extensibility](#future-proof-and-extensibility) section for the process on how to include a new key to the `reactNativeManifest` section. + +## Excluded use cases + +This section lists the use cases where we believe the `reactNativeManifest` section should **not** be used. + +### React Native version support + +For the time being, we're not planning to add a `version` section to the `reactNativeManifest` section for libraries. The rationale is that we believe that the `react-native` version should be specified in either the `peerDependencies` or the `engines` section of the `package.json` of the project. This follows the idiomatic way to specify the version of a dependency in a NodeJS project. + +### TurboModule/Fabric toggles + +At the time of writing, we prefer to don't offer a dedicated section to toggle Fabric/TurboModule capability inside `reactNativeManifest`. The rationale is that we believe that the `newArch` section is sufficient to expose the New Architecture to users. +Selectively toggling Fabric/TurboModule is a more advanced feature. We believe we'll still be offering a more advanced way to feature toggle such infrastructure pieces, but at the current state, our preference is to not expose such capability in the top level `reactNativeManifest` section. + +## Proposed Tooling + +This section lists the tooling we believe could be built on top of `reactNativeManifest` to improve the developer experience. + +### `align-deps` support + +This RFC originated from a [conversation between Meta and Microsoft](https://github.com/microsoft/rnx-kit/issues/1863) to use `align-deps` as key feature in the New Architecture rollout support, in helping developers understanding if a library they're using is New Architecture compatible or not. Very quickly it became clear that there is currently no straightforward way to know if a library indeed has that support. + +Therefore we believe `align-deps` can benefit from this information and be extended to offer New Architecture support information. +### React Native CLI support + +Ideally the CLI (or any other build tool) should be able to consume the `reactNativeManifest` section and warn at build time if a configuration is invalid (either by having an invalid value or by having a mixture of invalid configurations). + +It's outside the scope of this RFC to delve into details of which tool will implement which logic and we defer this to a future discussion. + +### React Native Directory + +The React Native direcotry can easily consume the `reactNativeManifest` by: +1. Query the NPM registry with [their API](https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md) to obtain the repository information. +2. Query the repository information to obtain the `packge.json` and read the `reactNativeManifest` section. + +## Future proof and extensibility + +We believe that the `reactNativeManifest` section should be extensible as we believe new capabilities and features can be added there. +Specifically, we envision to evolve `reactNativeManifest` or `reactNativeManifest.capabilites` as follows: + +1. Each addition of a new capability [requires a new RFC](https://github.com/react-native-community/discussions-and-proposals#proposals) to be approved. +2. Each RFC should define a new section in the `reactNativeManifest` with what's the intended use case. +3. Each RFC should define what are the sensible defaults and should refrain from breaking changes. +4. Each RFC should define the deprecation strategy for previous flags/features related to the same capability. + +## Complete Example + +Here we present a full example of how a `reactNativeManifest` section could look like for a library and an app. + +For an app the section will look as follows: + ```json { - "name": "contoso", - "version": "2.0.3", - ..., - "reactNativeMetadata": { + "name": "my-awesome-app", + "version": "1.2.3", + "reactNativeManifest": { "version": "1.0", "type": "app", - "features": { - "newArchEnabled": "true", + "capabilities": { + "hermes": { + "enabled": true + }, + "newArch": { + "enabled": true + }, + "codegenConfig": { + "name": "AppSpecs", + "type": "all", + "jsSrcsDir": ".", "android": { - "codegen": "true", - "turbomodules": "true", - }, - // Future example - // "metroExportsMode": "strict" - }, - ... + "javaPackageName": "com.facebook.fbreact.specs" + } + } } + } +} ``` -## Motivation - -The conversation that originated this idea was [an ask from the Meta team](https://github.com/microsoft/rnx-kit/issues/1863) to have `align-deps` help developers in the community migrating to the new architecture by providing insights into which libraries would have Fabric/TM support. Very quickly it became clear that there is currently no straightforward way to know if a library indeed has that support. - -A similar problem was also encountered by [React Native Directory](https://reactnative.directory/), that tried to [programmatically infer](https://github.com/react-native-community/directory/pull/870) support via the presence of certain fields in the package.json of a library - this because one can get access to the manifest file via npm without having to download the entire node_module. - -It became clear that there was a need to be able to communicate and parse certain information about a react-native library via its `package.json`, which led to [a meeting](https://github.com/microsoft/rnx-kit/discussions/2125) between Meta, Microsoft and Expo to figure out a common solution. - -This RFC is the result of that meeting: a new section of the `package.json`, called `reactNativeMetadata`, to contain certain metadata about a react-native project - it being either a library or an app. - -There are a few areas that this section could express in a consistent manner; here's a list of the ones we could think of (in no particular order): - -- if a library supports the new architecture, turbo modules or fabric or both -- which versions of react-native a library supports in a given release -- if an app has opted in into Fabric, TM, or both -- if an app is using Hermes or another js engine - -Sidenote: the name `reactNativeMetadata` was chosen to mirror the camel case pattern used by other fields such as `peerDependenciesMeta`. - -## Detailed design - -This is the section that in this first draft will need to be properly finalised. For now, here's what we envisioned: - -```js -reactNativeMetadata: { - version: string, // ex. 1.0.0 - we know that this spec might evolve in the future - type: string; // "app" or "library" - features: { - // can be global... - newArchEnabled: boolean, - jsEngine: string, // ex. "hermes", "jsc", "V8"... - // ...or platform specific - android: { - newArchEnabled: boolean, - fabric: boolean, - turbomodules: boolean, - jsEngine: string, - }, - ios: { - ... // same as android - }, - macos: { - ... // same as android - }, - windows: { - ... // same as android - }, - // more platforms, params - add a comment here - }, - // this section would most likely only be relevant for libraries - requirements: { - "react-native": string, // semver compliant range of versions of RN supported - expo: string, // semver compliant range of versions of Expo supported (if any) +For a library the section will look as follows: + +```json +{ + "name": "my-awesome-library", + "version": "1.2.3", + "reactNativeManifest": { + "version": "1.0", + "type": "library", + "capabilities": { + "hermes": { + "enabled": true + }, + "newArch": { + "enabled": true + }, + "codegenConfig": { + "name": "LibrarySpecs", + "type": "all", + "jsSrcsDir": ".", + "android": { + "javaPackageName": "com.facebook.fbreact.specs" + } + } } + } } ``` -## Drawbacks +### JSON Schema -The main drawback is that we'd need to convince developers to start using it, so we'd have to expect a phase in which only a part of the userbase has this section configured and tool developers need to account for the section missing entirely. +The JSON Schema will be published as soon as we find an agreement on the key names and semantics. ## Alternatives -`reactNativeMetadata` is, by design, fully original and new: there are other files that are used to define configurations for various aspect of react-native based projects (such as `react-native.config.js`, `app.json`, Expo's `app.config.js`, `expo-module.config.json`) but this one does **not** overlap with any of them. Explicitly, this one has the purpose of being filled only with metadata to be read by package managers and other tools. +`reactNativeManifest` is, by design, fully original and new: there are other files that are used to define configurations for various aspect of react-native based projects (such as `react-native.config.js`, `app.json`, Expo's `app.config.js`, `expo-module.config.json`) but this one does **not** overlap with any of them. Explicitly, this one has the purpose of being filled only with capabilities and metadata to be read by package managers and build tools. ## Adoption strategy -Once the v1.0.0 version of the shape is finalised, we can start integrating it across the board in tools such has `align-deps` and the RNDirectory, and in the react-native tools themselves. This first wave of support would have to be coordinated around a certain release that lives in RN's main branch; for example, if we were to introduce it now, it'd be in 0.72 (which branch has not been cut) via the app template. +If we decide to adopt this proposal, we'll have to broadcast this change to the ecosystem. +Ideally, we'll start by adding the `reactNativeManifest` section to the app template and to `create-react-native-library`. + +We foresee a phase in which only a part of the userbase has this section configured and tool developers need to account for the absence of this section. + +Once the v1.0.0 version of the shape is finalised, we can start integrating it across the board in various tools mentioned in [Proposed Tooling](#proposed-tooling) section. This first wave of support would have to be aligned around a given React Native release, say in 0.73 (which branch has not been cut). + +After adding this first wave of support, and related documentation, we could more broadly communicate to the community and the maintainers how adding this section would benefit them too. -After adding this first wave of support, and related documentation, we could more broadly communicate to the community and the maintainers how adding this section would benefit them too. Especially for library maintainers we'll have another RFC coming up soon to describe how we are planning to better communicate this. +### Documentation update needed -## How we teach this +If we agree on this proposal, we'll have to: -- add to core's documentation (which should happen anyway if we start using it for flags for new arch configuration) -- add to the templates (both app and library) -- have RNDirectory and align-deps mention a call to action to introduce this +- Update the `reactnative.dev` website to mention this (and harmonize the current New Architecture and Hermes configuration pages) +- Update the app/library templates as mentioned +- Potentially add a CTA banner to React Native Directory and `align-deps` to introduce this field. -## Unresolved questions +## Open questions -v1.0.0 of the shape needs to be finalized. +* `v1.0.0` of the schema needs to be finalized and agreed by stakeholders. From 891121411969259d6677881bcc24d92e06f1b0d7 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 6 Apr 2023 14:43:42 +0200 Subject: [PATCH 19/25] Clarify version schema --- proposals/0012-introduce-reactNativeMetadata.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/proposals/0012-introduce-reactNativeMetadata.md b/proposals/0012-introduce-reactNativeMetadata.md index 36660312..aba23310 100644 --- a/proposals/0012-introduce-reactNativeMetadata.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -227,6 +227,8 @@ The React Native direcotry can easily consume the `reactNativeManifest` by: ## Future proof and extensibility +We're versioning the schema using the `schema` key in the `reactNativeManifest` section. This allows us to evolve the schema in the future without breaking changes. We believe that the `schema` key should be a semver version string. + We believe that the `reactNativeManifest` section should be extensible as we believe new capabilities and features can be added there. Specifically, we envision to evolve `reactNativeManifest` or `reactNativeManifest.capabilites` as follows: @@ -246,7 +248,7 @@ For an app the section will look as follows: "name": "my-awesome-app", "version": "1.2.3", "reactNativeManifest": { - "version": "1.0", + "schema": "1.0.0", "type": "app", "capabilities": { "hermes": { @@ -275,7 +277,7 @@ For a library the section will look as follows: "name": "my-awesome-library", "version": "1.2.3", "reactNativeManifest": { - "version": "1.0", + "schema": "1.0.0", "type": "library", "capabilities": { "hermes": { From 0b0e3cdca84f4f24fdd5c6dbe16f96a7ff194e84 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 6 Apr 2023 14:48:22 +0200 Subject: [PATCH 20/25] Add a build logic algoritm section --- proposals/0012-introduce-reactNativeMetadata.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/proposals/0012-introduce-reactNativeMetadata.md b/proposals/0012-introduce-reactNativeMetadata.md index aba23310..d21ec628 100644 --- a/proposals/0012-introduce-reactNativeMetadata.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -207,6 +207,17 @@ Selectively toggling Fabric/TurboModule is a more advanced feature. We believe w This section lists the tooling we believe could be built on top of `reactNativeManifest` to improve the developer experience. +### Build Logic support + +Build tools such as Gradle/CocoaPods or others should account for the `reactNativeManifest` section and use it to configure the build pipeline accordingly to this and future RFCs. Specifically they should follow this logic for a generic capability `foo`. + +1. If the capability `foo` is specificed in the `reactNativeManifest.capabilities` section, use the value specified in the `reactNativeManifest.capabilities.foo` section. + 1. If the user is **also** specifying the capability `foo` in the build tool specific configuration (e.g. inside `gradle.properties` for Android) behave as follows: + 1. If the two values are **compatible**, use them without notifying the user. + 1. If the two values are **incompatible**, notify the user and use the value specified in the `reactNativeManifest.capabilities.foo` section will prevail and the value specified in the build tool specific configuration will be ignored. + 1. If the user is **not** specifying the capability `foo` in the build tool specific configuration, honor the value specified in the `reactNativeManifest.capabilities.foo` section. +1. If the capability `foo` is **not** specificed in the `reactNativeManifest.capabilities` section, honor the value specified in the build tool specific configuration or the default value if not specified. + ### `align-deps` support This RFC originated from a [conversation between Meta and Microsoft](https://github.com/microsoft/rnx-kit/issues/1863) to use `align-deps` as key feature in the New Architecture rollout support, in helping developers understanding if a library they're using is New Architecture compatible or not. Very quickly it became clear that there is currently no straightforward way to know if a library indeed has that support. From 4b41f0c8b69050fc0b5ec136d648e5447e640ad5 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 6 Apr 2023 14:49:41 +0200 Subject: [PATCH 21/25] Add clarification on jsonschema --- proposals/0012-introduce-reactNativeMetadata.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0012-introduce-reactNativeMetadata.md b/proposals/0012-introduce-reactNativeMetadata.md index d21ec628..39f37b0b 100644 --- a/proposals/0012-introduce-reactNativeMetadata.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -227,6 +227,7 @@ Therefore we believe `align-deps` can benefit from this information and be exten ### React Native CLI support Ideally the CLI (or any other build tool) should be able to consume the `reactNativeManifest` section and warn at build time if a configuration is invalid (either by having an invalid value or by having a mixture of invalid configurations). +For example, the CLI could use [jsonschema](https://npmjs.com/package/jsonschema) at build time and validate the `package.json` against the schema we provide. It's outside the scope of this RFC to delve into details of which tool will implement which logic and we defer this to a future discussion. From 7c4ccb80b22ebbbbb79fac3ef185e72f18d159ab Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Thu, 6 Jul 2023 15:11:44 +0200 Subject: [PATCH 22/25] Fix typos --- .../0012-introduce-reactNativeMetadata.md | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/proposals/0012-introduce-reactNativeMetadata.md b/proposals/0012-introduce-reactNativeMetadata.md index 39f37b0b..7ab00cf8 100644 --- a/proposals/0012-introduce-reactNativeMetadata.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -1,8 +1,8 @@ --- -title: [DRAFT] Introducing `reactNativeManifest` to `package.json` for React Native specific metadata +title: DRAFT | Introducing `reactNativeManifest` to `package.json` for React Native specific metadata author: - - Lorenzo Sciandra - - Nicola Corti +- Lorenzo Sciandra +- Nicola Corti date: 25-01-2021 --- @@ -27,24 +27,24 @@ As of today, there is no simple programmatic way to know if a NPM package is a R Those indicators are all non-exhaustive. Similarly, there is no easy way to understand if a library is exposing a native module or not. -The React Native CLI is facing this difficulty and tries to circumvent it by inspecting the native code and search for subclasses of `*ReactPackage` using complex regular expressions ([example](https://github.com/react-native-community/cli/blob/605c542d18efcb02f217d3c85726fa73a79054c2/packages/cli-platform-android/src/config/findPackageClassName.ts#L35)) due to the lack of a declarative way to express this information. +The React Native CLI is facing this difficulty and tries to circumvent it by inspecting the native code and searching for subclasses of `*ReactPackage` using complex regular expressions ([example](https://github.com/react-native-community/cli/blob/605c542d18efcb02f217d3c85726fa73a79054c2/packages/cli-platform-android/src/config/findPackageClassName.ts#L35)) due to the lack of a declarative way to express this information. -Another practical examples of this need are the [React Native Directory](https://reactnative.directory/) attempting to [programmatically infer](https://github.com/react-native-community/directory/pull/870) New Architecture support from the presence of the `codegenConfig` fields in the `package.json` of a library. The `codegenConfig` filed is not intended to carry semantic information about New Architecture support, and is definitely a non-exhaustive solution for this problem. +Another practical example of this need is the [React Native Directory](https://reactnative.directory/) attempting to [programmatically infer](https://github.com/react-native-community/directory/pull/870) the New Architecture support from the presence of the `codegenConfig` fields in the `package.json` of a library. The `codegenConfig` field is not intended to carry semantic information about New Architecture support, and is definitely a non-exhaustive solution for this problem. -While we can continue to build tooling that tries to infer metadata and capabilities of a library, providing a declarative way to declared such capabilites provides a series of benefit: +While we can continue to build tooling that tries to infer metadata and capabilities of a library, providing a declarative way to describe such capabilities yields a series of benefit: * **predictability**: developers can easily understand what a library is capable of doing and if such capability matches their app. * **tooling**: tooling can easily consume this information and provide a better experience to developers. * **single source of thruth**: a single manifest will act as a source of truth for all those capabilities and flag definition, that can easily be consumed by different platforms to provide a unified way to enable/disable capabilities. -* **verifiability**: the manifest can be easily verified & linted by tooling to ensure that app & library are not using an incompatible set of compatibilites. Similarly, the JSON schema we publish can be used to validate the `reactNativeManifest` section. +* **verifiability**: the manifest can be easily verified and linted by tooling to ensure that apps and libraries are not using an incompatible set of dependencies. Similarly, the JSON schema we publish can be used to validate the `reactNativeManifest` section. ## Use Cases -This section lists the use case we envision for the `reactNativeManifest` section. +This section lists the use cases we envision for the `reactNativeManifest` section. ### Declaring the type of a project For the sake of this RFC, we consider a React Native project as either an app or a library. -While an app project can contain native modules and components, it's **not** possible for a app to "consume" another app as a library as this would break the native build system invariants (i.e. a single `AndroidManifest.xml` and `Info.plist` file per app, etc.). If an app projects wishes to distribute some of its native modules or components, they need to be extracted into a library project. +While an app project can contain native modules and components, it's **not** possible for an app to "consume" another app as a library. This would break the native build system invariants (i.e. a single `AndroidManifest.xml` and `Info.plist` file per app, etc.). If an app projects wishes to distribute some of its native modules or components, they need to be extracted into a library project. We're proposing to declare if a project is either a library or an app by adding the `type` field (as `"app"` or `"library"`) to the `reactNativeManifest` section of the `package.json` file. @@ -59,19 +59,19 @@ We're proposing to declare if a project is either a library or an app by adding ### New Architecture support One of the primary driver of this proposal is the [New React Native Architecture support](https://reactnative.dev/docs/the-new-architecture/landing-page). Specifically we believe that the ecosystem is currently affected by: -* Lack of a declarative way to define if a library is compatible with New Architecture - * As described in the [rationale](#rationale), tooling needs to infer this information from the presence of the `codegenConfig` field in the `package.json` of a library. Libraries might decide to don't use codegen and still be compatible with New Architecture, so this method is not exhaustive. - * The alternative at this stage is to inspect the code and check the API callsites if they are New Architecture or Old Architecture compatible. -* Lack of a declarative way to enable New Architecture on both platforms - * Currently New Architecture is enabled by a Gradle Property `newArchEnabled` on Android in the `gradle.properties` file and by invokign `RCT_NEW_ARCH_ENABLED pod install` on iOS. - * As of today, there is no way to enable New Architecture for an app project for both platforms - * Moreover, the file vs environment variable leads to a scenario where you can't know if a app supports New Architecture or not by inspecting the code (as New Architecture support is known at `pod install` time and is not codified in the codebase) +* Lack of a declarative way to define if a library is compatible with the New Architecture + * As described in the [rationale](#rationale), tooling needs to infer this information from the presence of the `codegenConfig` field in the `package.json` of a library. Libraries might decide not to use Codegen and still be compatible with the New Architecture, so this method is not exhaustive. + * The alternative at this stage is to inspect the code and check whether the API callsites are New Architecture or Old Architecture compatible. +* Lack of a declarative way to enable the New Architecture on both platforms + * Currently the New Architecture is enabled by a Gradle Property `newArchEnabled` on Android in the `gradle.properties` file and by invoking `RCT_NEW_ARCH_ENABLED=1 bundle exec pod install` on iOS. + * As of today, there is no way to enable the New Architecture for an app project for both platforms + * Moreover, the difference in how the New Architecture is enabled leads to a scenario where you can't statically know if an app supports New Architecture or not (as the New Architecture support is known at `pod install` time and is not codified in the codebase) Therefore we propose to add the `newArch` section to the `reactNativeManifest.capabilities` of **both apps and libraries** with the following semantic: -* For Apps: `newArch.enabled==true` implies that the app wants to use the New Architecture. -* For Libraries: `newArch.enabled==true` implies that the library is compatible with the New Architecture. +* For Apps: `newArch.enabled==true` means that the app wants to use the New Architecture. +* For Libraries: `newArch.enabled==true` means that the library is compatible with the New Architecture. -Tools can be built on top of this information to check that a app with `newArch.enabled==true` is not accepting libraries with `newArch.enabled==false` and warning against library that don't have the key specified. +Tools can be built on top of this information to check that an app with `newArch.enabled==true` is not accepting libraries with `newArch.enabled==false` and to warn against library that don't have the key specified. The setup would look as follows for both apps and libraries: @@ -87,7 +87,7 @@ The setup would look as follows for both apps and libraries: } ``` -This section will allows also for split configuration between platforms: +This section will allow also for split configuration between platforms: ```json { @@ -112,7 +112,7 @@ This section will allows also for split configuration between platforms: #### `codegenConfig` support -Similarly to New Architecture support metadata, the `codegenConfig` is a key metadata of the New Architecture build pipeline. +Similarly to the New Architecture support metadata, the `codegenConfig` is a key metadata of the New Architecture build pipeline. Currently the `codegenConfig` is a **top-level** key in the `package.json` of a project. We propose to move this key under the `reactNativeManifest.capabilites` section as follows: @@ -135,7 +135,7 @@ We propose to move this key under the `reactNativeManifest.capabilites` section ### Hermes Support -Similarly to New Architecture support, the current way to enable/disable the Hermes engine is toggled by using a Gradle Property `hermesEnabled` on Android and changing the `:hermes_enabled` property in the Podfile. +Similarly to the New Architecture support, the current way to enable/disable the Hermes engine is toggled by using a Gradle Property `hermesEnabled` on Android and changing the `:hermes_enabled` property in the Podfile. We propose to add the `hermes` section under the `reactNativeManifest.capabilities` section as follows: @@ -200,8 +200,8 @@ For the time being, we're not planning to add a `version` section to the `reactN ### TurboModule/Fabric toggles -At the time of writing, we prefer to don't offer a dedicated section to toggle Fabric/TurboModule capability inside `reactNativeManifest`. The rationale is that we believe that the `newArch` section is sufficient to expose the New Architecture to users. -Selectively toggling Fabric/TurboModule is a more advanced feature. We believe we'll still be offering a more advanced way to feature toggle such infrastructure pieces, but at the current state, our preference is to not expose such capability in the top level `reactNativeManifest` section. +At the time of writing, we prefer not to offer a dedicated section to toggle Fabric/TurboModule capability inside `reactNativeManifest`. The rationale is that we believe that the `newArch` section is sufficient to expose the New Architecture to users. +Selectively toggling Fabric/TurboModule is a more advanced feature. We believe we'll still be offering a more advanced way to enable or disable those pieces of the New Architecture infrastructure, but at the current state, our preference is not to expose such capability in the top level `reactNativeManifest` section. ## Proposed Tooling @@ -220,9 +220,9 @@ Build tools such as Gradle/CocoaPods or others should account for the `reactNati ### `align-deps` support -This RFC originated from a [conversation between Meta and Microsoft](https://github.com/microsoft/rnx-kit/issues/1863) to use `align-deps` as key feature in the New Architecture rollout support, in helping developers understanding if a library they're using is New Architecture compatible or not. Very quickly it became clear that there is currently no straightforward way to know if a library indeed has that support. +This RFC originated from a [conversation between Meta and Microsoft](https://github.com/microsoft/rnx-kit/issues/1863) to use `align-deps` as key feature in the New Architecture rollout support, in helping developers understand if a library they're using is compatible with the New Architecture or not. Very quickly it became clear that there is currently no straightforward way to know if a library indeed has that support. -Therefore we believe `align-deps` can benefit from this information and be extended to offer New Architecture support information. +Therefore we believe `align-deps` can benefit from this information and can be extended to offer New Architecture support information. ### React Native CLI support @@ -247,7 +247,7 @@ Specifically, we envision to evolve `reactNativeManifest` or `reactNativeManifes 1. Each addition of a new capability [requires a new RFC](https://github.com/react-native-community/discussions-and-proposals#proposals) to be approved. 2. Each RFC should define a new section in the `reactNativeManifest` with what's the intended use case. 3. Each RFC should define what are the sensible defaults and should refrain from breaking changes. -4. Each RFC should define the deprecation strategy for previous flags/features related to the same capability. +4. Each RFC should define the deprecation strategy for previous flags/features related to the same capability, if needed. ## Complete Example @@ -324,7 +324,7 @@ The JSON Schema will be published as soon as we find an agreement on the key nam If we decide to adopt this proposal, we'll have to broadcast this change to the ecosystem. Ideally, we'll start by adding the `reactNativeManifest` section to the app template and to `create-react-native-library`. -We foresee a phase in which only a part of the userbase has this section configured and tool developers need to account for the absence of this section. +We foresee a phase in which only a part of the userbase has this section configured, and tool and developers need to account for the absence of this section. Once the v1.0.0 version of the shape is finalised, we can start integrating it across the board in various tools mentioned in [Proposed Tooling](#proposed-tooling) section. This first wave of support would have to be aligned around a given React Native release, say in 0.73 (which branch has not been cut). From d4cacd6f15e95e8021d99dec1732bbdbd582115d Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Fri, 6 Oct 2023 10:49:08 +0100 Subject: [PATCH 23/25] Revamp React Native Manifest proposal --- .../0012-introduce-reactNativeMetadata.md | 108 ++++++++++++++---- 1 file changed, 83 insertions(+), 25 deletions(-) diff --git a/proposals/0012-introduce-reactNativeMetadata.md b/proposals/0012-introduce-reactNativeMetadata.md index 7ab00cf8..e8487643 100644 --- a/proposals/0012-introduce-reactNativeMetadata.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -3,6 +3,7 @@ title: DRAFT | Introducing `reactNativeManifest` to `package.json` for React Nat author: - Lorenzo Sciandra - Nicola Corti +- Riccardo Cipolleschi date: 25-01-2021 --- @@ -14,24 +15,33 @@ date: 25-01-2021 This RFC introduces and formally discuss the concept of _React Native Manifest_, a set of metadata about a React Native project (either app or library). This metadata can be consumed by tooling to enhance the developer experience and support the React Native ecosystem. +This would be the entry point for all the official configurations supported by the core of React Native. Examples (not an exhaustive list) of these configurations could be: +* Whether the New Architecture is enabled or not. +* Which JS Engine should run in the app. +This will not be the place where we can add any kind of configuration. + Practically, this RFC proposes to add a `reactNativeManifest` section to the `package.json` file of a React Native app or library. This new section will allow developers to follow a declarative approach to express capabilities of apps and libraries in a more formalized manner. ## Rationale -The need to specify **capabilities and metadata** for a react-native library is inherent to the React Native ecosystem. +The need to specify **capabilities and metadata** for a react-native library is inherent to the React Native ecosystem. This need is so evident that various frameworks provided their own ways to specify those values. Look at `react-native.config.js` for the CLI or `expo.config.js` for Expo. -As of today, there is no simple programmatic way to know if a NPM package is a React Native library. Good indicators could be: +Another issue is that, as of today, there is no simple programmatic way to know if a NPM package is a React Native library. +We have some heuristics, though: * Presence of a `react-native:*` dependency in the `peerDependencies` section of the `package.json` ([example](https://github.com/RonRadtke/react-native-blob-util/blob/80628d418a5c81439dc2c1a1f05fae6406f0ba7f/package.json#L46C10-L49)) * Presence of React Native specific files such as `react-native.config.js` files -Those indicators are all non-exhaustive. +Those indicators are all non-exhaustive. -Similarly, there is no easy way to understand if a library is exposing a native module or not. -The React Native CLI is facing this difficulty and tries to circumvent it by inspecting the native code and searching for subclasses of `*ReactPackage` using complex regular expressions ([example](https://github.com/react-native-community/cli/blob/605c542d18efcb02f217d3c85726fa73a79054c2/packages/cli-platform-android/src/config/findPackageClassName.ts#L35)) due to the lack of a declarative way to express this information. +Similarly, there is no easy way to understand if a library is exposing a native module, a native component or not. +The lack of this information pushed the community to create workarounds to understand that. For Example, the React Native CLI tries to circumvent it by inspecting the native code and searching for subclasses of `*ReactPackage` using complex regular expressions ([example](https://github.com/react-native-community/cli/blob/605c542d18efcb02f217d3c85726fa73a79054c2/packages/cli-platform-android/src/config/findPackageClassName.ts#L35)) due to the lack of a declarative way to express this information. Another practical example of this need is the [React Native Directory](https://reactnative.directory/) attempting to [programmatically infer](https://github.com/react-native-community/directory/pull/870) the New Architecture support from the presence of the `codegenConfig` fields in the `package.json` of a library. The `codegenConfig` field is not intended to carry semantic information about New Architecture support, and is definitely a non-exhaustive solution for this problem. -While we can continue to build tooling that tries to infer metadata and capabilities of a library, providing a declarative way to describe such capabilities yields a series of benefit: +While we can continue to build tooling that tries to infer metadata and capabilities of a library, all these approach would never be precise and could provide misleading information. + +We suggest to provide a declarative way to describe such capabilities. +This approach would yield several benefits: * **predictability**: developers can easily understand what a library is capable of doing and if such capability matches their app. * **tooling**: tooling can easily consume this information and provide a better experience to developers. * **single source of thruth**: a single manifest will act as a source of truth for all those capabilities and flag definition, that can easily be consumed by different platforms to provide a unified way to enable/disable capabilities. @@ -56,6 +66,8 @@ We're proposing to declare if a project is either a library or an app by adding } ``` +This is a mandatory field that needs to be specified in all the apps and libraries. + ### New Architecture support One of the primary driver of this proposal is the [New React Native Architecture support](https://reactnative.dev/docs/the-new-architecture/landing-page). Specifically we believe that the ecosystem is currently affected by: @@ -63,15 +75,15 @@ One of the primary driver of this proposal is the [New React Native Architecture * As described in the [rationale](#rationale), tooling needs to infer this information from the presence of the `codegenConfig` field in the `package.json` of a library. Libraries might decide not to use Codegen and still be compatible with the New Architecture, so this method is not exhaustive. * The alternative at this stage is to inspect the code and check whether the API callsites are New Architecture or Old Architecture compatible. * Lack of a declarative way to enable the New Architecture on both platforms - * Currently the New Architecture is enabled by a Gradle Property `newArchEnabled` on Android in the `gradle.properties` file and by invoking `RCT_NEW_ARCH_ENABLED=1 bundle exec pod install` on iOS. + * Currently the New Architecture is enabled by a Gradle Property `newArchitectureEnabled` on Android in the `gradle.properties` file and by invoking `RCT_NEW_ARCH_ENABLED=1 bundle exec pod install` on iOS. * As of today, there is no way to enable the New Architecture for an app project for both platforms * Moreover, the difference in how the New Architecture is enabled leads to a scenario where you can't statically know if an app supports New Architecture or not (as the New Architecture support is known at `pod install` time and is not codified in the codebase) -Therefore we propose to add the `newArch` section to the `reactNativeManifest.capabilities` of **both apps and libraries** with the following semantic: -* For Apps: `newArch.enabled==true` means that the app wants to use the New Architecture. -* For Libraries: `newArch.enabled==true` means that the library is compatible with the New Architecture. +Therefore we propose to add the `newArchitecture` section to the `reactNativeManifest.capabilities` of **both apps and libraries** with the following semantic: +* For Apps: `newArchitecture.enabled==true` means that the app wants to use the New Architecture. +* For Libraries: `newArchitecture.enabled==true` means that the library is compatible with the New Architecture. -Tools can be built on top of this information to check that an app with `newArch.enabled==true` is not accepting libraries with `newArch.enabled==false` and to warn against library that don't have the key specified. +Tools can be built on top of this information to check that an app with `newArchitecture.enabled==true` is not accepting libraries with `newArchitecture.enabled==false` and to warn against library that don't have the key specified. The setup would look as follows for both apps and libraries: @@ -79,7 +91,7 @@ The setup would look as follows for both apps and libraries: { "reactNativeManifest": { "capabilities": { - "newArch": { + "newArchitecture": { "enabled": true } } @@ -94,14 +106,14 @@ This section will allow also for split configuration between platforms: "reactNativeManifest": { "android": { "capabilities": { - "newArch": { + "newArchitecture": { "enabled": true } } }, "ios": { "capabilities": { - "newArch": { + "newArchitecture": { "enabled": true } } @@ -110,10 +122,35 @@ This section will allow also for split configuration between platforms: } ``` +If omitted, the `newArchitecture.enabled` would have a value of `false` (this may change in the future). + +The precedence rules are that platform-specific settings override general settings. So, for example: + +```json +{ + "reactNativeManifest": { + "capabilities": { + "newArchitecture": { + "enabled": true + } + }, + "android": { + "capabilities": { + "newArchitecture": { + "enabled": false + } + } + }, + } +} +``` + +would means that, in general, the app/library support the New Architecture, but not for Android. + #### `codegenConfig` support Similarly to the New Architecture support metadata, the `codegenConfig` is a key metadata of the New Architecture build pipeline. -Currently the `codegenConfig` is a **top-level** key in the `package.json` of a project. +Currently the `codegenConfig` is a **top-level** key in the `package.json` of a project. We propose to move this key under the `reactNativeManifest.capabilites` section as follows: ```json @@ -174,9 +211,34 @@ which will also allow for split configuration between platforms as follows: } ``` +If omitted, the `hermes.enabled` would have a value of `true`. + +The precedence rules are that platform-specific settings override general settings. So, for example: + +```json +{ + "reactNativeManifest": { + "capabilities": { + "hermes": { + "enabled": true + } + }, + "android": { + "capabilities": { + "hermes": { + "enabled": false + } + } + }, + } +} +``` + +would means that the app will run with Hermes for all the platforms it supports but not for Android. + ### React Native Release feature flagging -We currently have a numer of different files where capabilities of React Native can be toggled to enable/disable features in the runtime. +We currently have a number of different files where capabilities of React Native can be toggled to enable/disable features in the runtime. For example we have: * [android/gradle.properties](https://github.com/facebook/react-native/blob/main/packages/react-native/template/android/gradle.properties) to specify key-value build time properties for Android * [android/app/build.gradle](https://github.com/facebook/react-native/blob/main/packages/react-native/template/android/app/build.gradle) to specify build time configuration for Android @@ -200,7 +262,7 @@ For the time being, we're not planning to add a `version` section to the `reactN ### TurboModule/Fabric toggles -At the time of writing, we prefer not to offer a dedicated section to toggle Fabric/TurboModule capability inside `reactNativeManifest`. The rationale is that we believe that the `newArch` section is sufficient to expose the New Architecture to users. +At the time of writing, we prefer not to offer a dedicated section to toggle Fabric/TurboModule capability inside `reactNativeManifest`. The rationale is that we believe that the `newArchitecture` section is sufficient to expose the New Architecture to users. Selectively toggling Fabric/TurboModule is a more advanced feature. We believe we'll still be offering a more advanced way to enable or disable those pieces of the New Architecture infrastructure, but at the current state, our preference is not to expose such capability in the top level `reactNativeManifest` section. ## Proposed Tooling @@ -215,7 +277,6 @@ Build tools such as Gradle/CocoaPods or others should account for the `reactNati 1. If the user is **also** specifying the capability `foo` in the build tool specific configuration (e.g. inside `gradle.properties` for Android) behave as follows: 1. If the two values are **compatible**, use them without notifying the user. 1. If the two values are **incompatible**, notify the user and use the value specified in the `reactNativeManifest.capabilities.foo` section will prevail and the value specified in the build tool specific configuration will be ignored. - 1. If the user is **not** specifying the capability `foo` in the build tool specific configuration, honor the value specified in the `reactNativeManifest.capabilities.foo` section. 1. If the capability `foo` is **not** specificed in the `reactNativeManifest.capabilities` section, honor the value specified in the build tool specific configuration or the default value if not specified. ### `align-deps` support @@ -254,7 +315,7 @@ Specifically, we envision to evolve `reactNativeManifest` or `reactNativeManifes Here we present a full example of how a `reactNativeManifest` section could look like for a library and an app. For an app the section will look as follows: - + ```json { "name": "my-awesome-app", @@ -266,7 +327,7 @@ For an app the section will look as follows: "hermes": { "enabled": true }, - "newArch": { + "newArchitecture": { "enabled": true }, "codegenConfig": { @@ -292,10 +353,7 @@ For a library the section will look as follows: "schema": "1.0.0", "type": "library", "capabilities": { - "hermes": { - "enabled": true - }, - "newArch": { + "newArchitecture": { "enabled": true }, "codegenConfig": { @@ -324,7 +382,7 @@ The JSON Schema will be published as soon as we find an agreement on the key nam If we decide to adopt this proposal, we'll have to broadcast this change to the ecosystem. Ideally, we'll start by adding the `reactNativeManifest` section to the app template and to `create-react-native-library`. -We foresee a phase in which only a part of the userbase has this section configured, and tool and developers need to account for the absence of this section. +We foresee a phase in which only a part of the user base has this section configured, and tool and developers need to account for the absence of this section. Once the v1.0.0 version of the shape is finalised, we can start integrating it across the board in various tools mentioned in [Proposed Tooling](#proposed-tooling) section. This first wave of support would have to be aligned around a given React Native release, say in 0.73 (which branch has not been cut). From 0aefd3d66b2337f6c0f98dd74f31cc25ce0ec5e5 Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Tue, 17 Oct 2023 12:18:24 +0200 Subject: [PATCH 24/25] Address feedbacks from Community --- .../0012-introduce-reactNativeMetadata.md | 58 ++++++++++++++++--- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/proposals/0012-introduce-reactNativeMetadata.md b/proposals/0012-introduce-reactNativeMetadata.md index af8b3880..84e76c77 100644 --- a/proposals/0012-introduce-reactNativeMetadata.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -27,6 +27,8 @@ This new section will allow developers to follow a declarative approach to expre The need to specify **capabilities and metadata** for a react-native library is inherent to the React Native ecosystem. This need is so evident that various frameworks provided their own ways to specify those values. Look at `react-native.config.js` for the CLI or `app.json` for Expo. +Notice that we do not expect that frameworks stop using those files to configure **framework-specific** settings. What we would like to enforce with this proposal is to have single source of truth for what concern the React Native core settings. + Another issue is that, as of today, there is no simple programmatic way to know if a NPM package is a React Native library. We have some heuristics, though: * Presence of a `react-native:*` dependency in the `peerDependencies` section of the `package.json` ([example](https://github.com/RonRadtke/react-native-blob-util/blob/80628d418a5c81439dc2c1a1f05fae6406f0ba7f/package.json#L46C10-L49)) @@ -43,7 +45,7 @@ While we can continue to build tooling that tries to infer metadata and capabili We suggest to provide a declarative way to describe such capabilities. This approach would yield several benefits: * **predictability**: developers can easily understand what a library is capable of doing and if such capability matches their app. -* **tooling**: tooling can easily consume this information and provide a better experience to developers. +* **tooling**: tooling can easily consume this information and provide a better experience to developers. For example, we could tailor `microsoft/rnx-kit`'s align-deps tool to consume this information to help users double-check whether all their dependencies are supporting the New Architecture. See [`align-deps` support](#align-deps) below * **single source of thruth**: a single manifest will act as a source of truth for all those capabilities and flag definition, that can easily be consumed by different platforms to provide a unified way to enable/disable capabilities. * **verifiability**: the manifest can be easily verified and linted by tooling to ensure that apps and libraries are not using an incompatible set of dependencies. Similarly, the JSON schema we publish can be used to validate the `reactNativeManifest` section. @@ -147,6 +149,22 @@ The precedence rules are that platform-specific settings override general settin would means that, in general, the app/library support the New Architecture, but not for Android. +##### Planning forward: what happen when the New Architecture will be the default? + +The manifest is versioned, and we will use the versioning system to track breaking changes and different semantics. + +For version `1.0.0` the semantic would be: +* `newArchitecture` default value set to `false` + +In a future version, let's say `K.0.0`, the semantic would be: +* `newArchitecture` default value set to `true` +* `newArchitecture` field deprecated + +In a more future version, let's say `N.0.0` (where N > K), the semantic would be: +* `newArchitecture` field removed, together with the Old Architecture code. + +Notice that this system can also help us to track unmaintained/old packages: they would either not have the manifest at all or have old version of the manifest with its version's semantic. + #### `codegenConfig` support Similarly to the New Architecture support metadata, the `codegenConfig` is a key metadata of the New Architecture build pipeline. @@ -170,7 +188,7 @@ We propose to move this key under the `reactNativeManifest.capabilites` section } ``` -### Hermes Support +#### Hermes Support Similarly to the New Architecture support, the current way to enable/disable the Hermes engine is toggled by using a Gradle Property `hermesEnabled` on Android and changing the `:hermes_enabled` property in the Podfile. @@ -236,7 +254,7 @@ The precedence rules are that platform-specific settings override general settin would means that the app will run with Hermes for all the platforms it supports but not for Android. -### React Native Release feature flagging +#### React Native Release feature flagging We currently have a number of different files where capabilities of React Native can be toggled to enable/disable features in the runtime. For example we have: @@ -252,6 +270,32 @@ We propose to define `reactNativeManifest` as the **preferred entry point** for It's outside the scope of this RFC to declare all the new sections we intend to add, and we defer to the [Future proof and extensibility](#future-proof-and-extensibility) section for the process on how to include a new key to the `reactNativeManifest` section. +#### Out-of-Tree Platforms + +Although this proposal does not cover directly out-of-tree platforms, we strongly suggests maintainers of out-of-tree platforms to follow the same path. + +Tools written on top of the React Native Manifest would be able to work with iOS, Android and all the out of tree platforms that they support. They should follow the same precedence rules: if there is no platform specified, then the main flag would take precedence. If there is a platform specified, then that platform setup would have precedence. + +So, for example, given the manifest: + +```json +{ + "reactNativeManifest": { + "windows": { + "capabilities": { + "hermes": { + "enabled": false + } + } + }, + } +} +``` + +It means that for all the platforms but Windows, React Native will use Hermes as JS engine (`hermes.enabled` default value is `true`). For Windows specifically, it would be disabled. + +As an extra note, please be careful to **follow the same semantics** as the React Native Core: in this way we can avoid confusion for our users. + ## Excluded use cases This section lists the use cases where we believe the `reactNativeManifest` section should **not** be used. @@ -279,7 +323,7 @@ Build tools such as Gradle/CocoaPods or others should account for the `reactNati 1. If the two values are **incompatible**, notify the user and use the value specified in the `reactNativeManifest.capabilities.foo` section will prevail and the value specified in the build tool specific configuration will be ignored. 1. If the capability `foo` is **not** specificed in the `reactNativeManifest.capabilities` section, honor the value specified in the build tool specific configuration or the default value if not specified. -### `align-deps` support +### `align-deps` support This RFC originated from a [conversation between Meta and Microsoft](https://github.com/microsoft/rnx-kit/issues/1863) to use `align-deps` as key feature in the New Architecture rollout support, in helping developers understand if a library they're using is compatible with the New Architecture or not. Very quickly it became clear that there is currently no straightforward way to know if a library indeed has that support. @@ -294,9 +338,9 @@ It's outside the scope of this RFC to delve into details of which tool will impl ### React Native Directory -The React Native direcotry can easily consume the `reactNativeManifest` by: +The React Native directory can easily consume the `reactNativeManifest` by: 1. Query the NPM registry with [their API](https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md) to obtain the repository information. -2. Query the repository information to obtain the `packge.json` and read the `reactNativeManifest` section. +2. Query the repository information to obtain the `packge.json` and read the `reactNativeManifest` section. By putting these configurations in the package.json, React Native Directory can fetch only this file, rather than downloading the whole package and crawling its files. ## Future proof and extensibility @@ -384,7 +428,7 @@ Ideally, we'll start by adding the `reactNativeManifest` section to the app temp We foresee a phase in which only a part of the user base has this section configured, and tool and developers need to account for the absence of this section. -Once the v1.0.0 version of the shape is finalised, we can start integrating it across the board in various tools mentioned in [Proposed Tooling](#proposed-tooling) section. This first wave of support would have to be aligned around a given React Native release, say in 0.73 (which branch has not been cut). +Once the v1.0.0 version of the shape is finalised, we can start integrating it across the board in various tools mentioned in [Proposed Tooling](#proposed-tooling) section. This first wave of support would have to be aligned around a given React Native release, say in 0.74 (which branch has not been cut). After adding this first wave of support, and related documentation, we could more broadly communicate to the community and the maintainers how adding this section would benefit them too. From ccba7f4bb6f4650b30a0e61993851b3e0fd2e4e9 Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Tue, 14 Nov 2023 11:54:14 +0000 Subject: [PATCH 25/25] Apply feedback on new/oldArchitecture property --- .../0012-introduce-reactNativeMetadata.md | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/proposals/0012-introduce-reactNativeMetadata.md b/proposals/0012-introduce-reactNativeMetadata.md index 84e76c77..8c35f137 100644 --- a/proposals/0012-introduce-reactNativeMetadata.md +++ b/proposals/0012-introduce-reactNativeMetadata.md @@ -70,7 +70,7 @@ We're proposing to declare if a project is either a library or an app by adding This is a mandatory field that needs to be specified in all the apps and libraries. -### New Architecture support +### New and Old Architecture support One of the primary driver of this proposal is the [New React Native Architecture support](https://reactnative.dev/docs/the-new-architecture/landing-page). Specifically we believe that the ecosystem is currently affected by: * Lack of a declarative way to define if a library is compatible with the New Architecture @@ -126,6 +126,26 @@ This section will allow also for split configuration between platforms: If omitted, the `newArchitecture.enabled` would have a value of `false` (this may change in the future). +Notice that **libraries** might be supporting both architecture. So, the `capabilities` field can also support an `oldArchitecture` field of the same shape of the `newArchitecture`. The following example shows how to use both properties. + +```json +"reactNativeManifest": { + "capabilities": { + "newArchitecture": { + "enabled": true + }, + "oldArchitecture": { + "enabled": true + } + } +} +``` + +If omitted, the `oldArchitecture.enabled` would have a value of `true` (this may change in the future), to simplify the adoption of the manifest. In fact, the vast majority of React Native libraries are now supporting the Old Architecture and only a subset support both of them. + +The `oldArchitecture` property is **ignored** by apps: this is needed to avoid ambiguous setup where an app declares both architectures as enabled. +In other words, the `newArchitecture` property has always precedence in an app. + The precedence rules are that platform-specific settings override general settings. So, for example: ```json