From 226451d88ccc1cec8facd09d325759acd84adc1e Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Tue, 4 Jul 2023 14:08:44 +0200 Subject: [PATCH 01/22] React Native Standard API --- text/0002-react-native-standard-api.md | 146 +++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 text/0002-react-native-standard-api.md diff --git a/text/0002-react-native-standard-api.md b/text/0002-react-native-standard-api.md new file mode 100644 index 000000000..36a944d7a --- /dev/null +++ b/text/0002-react-native-standard-api.md @@ -0,0 +1,146 @@ +- Title: react-native-standard-api +- Date: 2023-07-04 +- RFC PR: (leave this empty) +- rnx-kit Issue: (leave this empty) + +## Abstract + +React Native currently lacks a well-defined, stable API. Today, we have a "wild +west" of community modules, each with its own set of interfaces and behaviours. +It is the developers' responsibility to find the module that fits their needs +and that is seemingly actively maintained. Additionally, the API are not +compatible with Web APIs, thus closing the door to a wealth of open source +libraries that do not have explicit React Native support. This often means that +developers cannot reuse existing web code, and must search for or even create +one for their needs. + +In this RFC, we are proposing to close this gap by providing our own +implementation of the +[Web APIs](https://developer.mozilla.org/en-US/docs/Web/API) for React Native. +The ultimate goal is to open the possibility to run non-UI code directly in a +React Native app; to provide a familiar environment for existing web developers +as well as a well-documented API for developers of any experience level. + +## Guide-level explanation + +We are aiming to have close to 100% compatibility with Web APIs. This means that +code such as below should just work out of box (or with minimal configuration). + +```js +// useBatteryLevel.js +function useBatteryLevel() { + const [batteryLevel, setBatteryLevel] = useState(-1); + useEffect(() => { + navigator.getBattery().then((battery) => setBatteryLevel(battery.level)); + }, [setBatteryLevel]); + return batteryLevel; +} +``` + +## Reference-level explanation + +Our goal is not to reimplement a browser, e.g. +[Electron](https://www.electronjs.org/). Apps made this way currently ship with +a full browser with everything that entails, including modules that they may +never get used. Native apps, and mobile apps especially, cannot be shipped with +unused bits; nor does it make any sense to include MBs of dependencies that are +never used. Ideally, migrating from community modules to the standard API should +not increase the final app size (at least not significantly). + +The API is implemented in layers: + +```mermaid +graph TD; + id(Web APIs)-->id1(JS shim); + id1(JS shim)-->id2(Turbo/native module); + id2(Turbo/native module)-->id3(Platform API/\nCommunity module); +``` + +- **Web APIs:** On the surface, there are little to no differences from normal + web code. + +- **JS shim:** This is a thin layer for marshalling web API calls to native + modules. It needs to be installed before it can be used. The current thinking + is to install this layer as polyfills. Installing them on-demand is preferred, + but we need to start somewhere and we should be able to improve this layer + later without affecting users. In any case, we will need tools to include + polyfills similarly to + [autolinking](https://github.com/react-native-community/cli/blob/main/docs/autolinking.md). + +- **Turbo/native module:** This is the layer that accepts JS calls and forwards + them platform implementations. A community module can be used here if one + exists. Modules are installed via the standard autolinking mechanism. + +### Modularity + +We want to avoid introducing unused modules and adding unnecessary bloat to the +app bundle. The API should therefore be broken down into smaller modules that +can be installed separately. These modules are installed by autolinking, and +must therefore be explicitly added to an app's `package.json`. + +Additionally, we want to avoid requiring that users manually add polyfills for +the modules they need. Instead, we propose that modules that implement a part of +the API to declare a polyfill in their `react-native.config.js`: + +```js +// react-native.config.js +module.exports = { + dependency: { + api: { + polyfill: "./polyfill.js", + }, + }, +}; +``` + +Polyfills are gathered and passed to Metro via +[`serializer.getPolyfills`](https://github.com/facebook/react-native/blob/0.72-stable/packages/metro-config/index.js#L49). + +### Discovery + +The number of modules is high. Finding which modules provide which part of Web +APIs can be overwhelming for consumers. We need tools that can tell users which +dependencies they need to add. At minimum we should: + +1. Implement a tool for detecting usage of web APIs + - The tool should be able to list used web APIs and report on uses that have + not been polyfilled. + - If possible, the tool should also recommend which dependencies to add + and/or automatically add it to `package.json`. + - Note: While we say "tool" here, it doesn't necessarily have to be a + standalone thing. A Babel plugin or similar would also fit. The less users + have to worry about it, the better. +2. Add new capabilities to + [`@rnx-kit/align-deps`](https://github.com/microsoft/rnx-kit/tree/main/packages/align-deps#readme) + - `align-deps` ensures that package versions are aligned and can help keeping + track of transitive dependencies. + +## Drawbacks + +> Why should we not do this, and what is the impact of that decision? Please +> consider the cost to educate developers, migrate existing users, and maintain +> the proposed changes. + +## Rationale, alternatives, and prior art + +- A variation of the current proposal without polyfills was considered, but it + would require users to change web code to accommodate native. For instance, + `navigator.getBattery()` would have to be rewritten as + `require("@react-native-api/battery-manager").getBattery()`. + +## Adoption strategy + +> If we accept this proposal, how will existing developers adopt it? Is this a +> breaking change? Can we write a codemod? Should we coordinate with other +> projects or libraries? + +## Unresolved questions + +- Which parts of the Web API do we prioritize first? + - As proof-of-concept, we suggest implementing + [`BatteryManager`](https://developer.mozilla.org/en-US/docs/Web/API/BatteryManager) + as it is small and self-contained. + - Android: + https://developer.android.com/training/monitoring-device-state/battery-monitoring + - iOS: + https://developer.apple.com/documentation/uikit/uidevice/1620051-batterystate From d9d634c93979620b97f04b6f680302d2f14f2d67 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Wed, 5 Jul 2023 10:36:55 +0200 Subject: [PATCH 02/22] add polyfill examples --- text/0002-react-native-standard-api.md | 36 +++++++++++++++++--------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/text/0002-react-native-standard-api.md b/text/0002-react-native-standard-api.md index 36a944d7a..b7400918e 100644 --- a/text/0002-react-native-standard-api.md +++ b/text/0002-react-native-standard-api.md @@ -64,8 +64,7 @@ graph TD; is to install this layer as polyfills. Installing them on-demand is preferred, but we need to start somewhere and we should be able to improve this layer later without affecting users. In any case, we will need tools to include - polyfills similarly to - [autolinking](https://github.com/react-native-community/cli/blob/main/docs/autolinking.md). + polyfills similarly to [autolinking][]. - **Turbo/native module:** This is the layer that accepts JS calls and forwards them platform implementations. A community module can be used here if one @@ -93,8 +92,7 @@ module.exports = { }; ``` -Polyfills are gathered and passed to Metro via -[`serializer.getPolyfills`](https://github.com/facebook/react-native/blob/0.72-stable/packages/metro-config/index.js#L49). +Polyfills are gathered and passed to Metro via [`serializer.getPolyfills`][]. ### Discovery @@ -110,16 +108,15 @@ dependencies they need to add. At minimum we should: - Note: While we say "tool" here, it doesn't necessarily have to be a standalone thing. A Babel plugin or similar would also fit. The less users have to worry about it, the better. -2. Add new capabilities to - [`@rnx-kit/align-deps`](https://github.com/microsoft/rnx-kit/tree/main/packages/align-deps#readme) +2. Add new capabilities to [`@rnx-kit/align-deps`][] - `align-deps` ensures that package versions are aligned and can help keeping track of transitive dependencies. ## Drawbacks -> Why should we not do this, and what is the impact of that decision? Please -> consider the cost to educate developers, migrate existing users, and maintain -> the proposed changes. +> TODO: Why should we not do this, and what is the impact of that decision? +> Please consider the cost to educate developers, migrate existing users, and +> maintain the proposed changes. ## Rationale, alternatives, and prior art @@ -127,12 +124,16 @@ dependencies they need to add. At minimum we should: would require users to change web code to accommodate native. For instance, `navigator.getBattery()` would have to be rewritten as `require("@react-native-api/battery-manager").getBattery()`. +- There are many polyfills out there, but they are mostly used to provide + functionalities that are only present in newer ES standards (e.g. + [`Object.assign`][], [`Object.is`][]). We have not found any that address the + scope defined here. ## Adoption strategy -> If we accept this proposal, how will existing developers adopt it? Is this a -> breaking change? Can we write a codemod? Should we coordinate with other -> projects or libraries? +> TODO: If we accept this proposal, how will existing developers adopt it? Is +> this a breaking change? Can we write a codemod? Should we coordinate with +> other projects or libraries? ## Unresolved questions @@ -144,3 +145,14 @@ dependencies they need to add. At minimum we should: https://developer.android.com/training/monitoring-device-state/battery-monitoring - iOS: https://developer.apple.com/documentation/uikit/uidevice/1620051-batterystate + + + +[`@rnx-kit/align-deps`]: + https://github.com/microsoft/rnx-kit/tree/main/packages/align-deps#readme +[`Object.assign`]: https://github.com/ljharb/object.assign/blob/main/polyfill.js +[`Object.is`]: https://github.com/es-shims/object-is/blob/main/polyfill.js +[`serializer.getPolyfills`]: + https://github.com/facebook/react-native/blob/0.72-stable/packages/metro-config/index.js#L49 +[autolinking]: + https://github.com/react-native-community/cli/blob/main/docs/autolinking.md From 0f4775e1b10e6c3ee1aeaa43d443734e784089d8 Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Thu, 6 Jul 2023 12:27:23 +0100 Subject: [PATCH 03/22] add Lorenzo's first pass, some more in the adoption strategy, and a couple of tweaks --- text/0002-react-native-standard-api.md | 47 +++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/text/0002-react-native-standard-api.md b/text/0002-react-native-standard-api.md index b7400918e..5b099e749 100644 --- a/text/0002-react-native-standard-api.md +++ b/text/0002-react-native-standard-api.md @@ -1,12 +1,12 @@ - Title: react-native-standard-api - Date: 2023-07-04 -- RFC PR: (leave this empty) -- rnx-kit Issue: (leave this empty) +- RFC PR: https://github.com/microsoft/rnx-kit/pull/2504 +- rnx-kit Issue: N/A ## Abstract React Native currently lacks a well-defined, stable API. Today, we have a "wild -west" of community modules, each with its own set of interfaces and behaviours. +west" of community modules, each with its own set of interfaces and behaviors. It is the developers' responsibility to find the module that fits their needs and that is seemingly actively maintained. Additionally, the API are not compatible with Web APIs, thus closing the door to a wealth of open source @@ -37,6 +37,9 @@ function useBatteryLevel() { } ``` +The native platforms we aim to support via react-native are iOS, Android, +Windows and macOS. + ## Reference-level explanation Our goal is not to reimplement a browser, e.g. @@ -67,8 +70,15 @@ graph TD; polyfills similarly to [autolinking][]. - **Turbo/native module:** This is the layer that accepts JS calls and forwards - them platform implementations. A community module can be used here if one - exists. Modules are installed via the standard autolinking mechanism. + them platform implementations. Modules are installed via the standard + autolinking mechanism. + +- **Platform API/Community module:** When available, we want to be able to + leverage what already exist in the community and avoid creating competing + solutions. When we need to write it from scratch, we want to comply with the + guidelines for the + [new architecture libraries](https://github.com/reactwg/react-native-new-architecture/discussions/categories/libraries) + provided by Meta. ### Modularity @@ -118,6 +128,11 @@ dependencies they need to add. At minimum we should: > Please consider the cost to educate developers, migrate existing users, and > maintain the proposed changes. +- existing React Native apps might need to be adapted to shift to this new + modules +- a lot of the tooling needed for this effort to succeed as detailed above needs + to be created + ## Rationale, alternatives, and prior art - A variation of the current proposal without polyfills was considered, but it @@ -128,6 +143,14 @@ dependencies they need to add. At minimum we should: functionalities that are only present in newer ES standards (e.g. [`Object.assign`][], [`Object.is`][]). We have not found any that address the scope defined here. +- [React Native Test App](https://github.com/microsoft/react-native-test-app) + will be used as the sandbox tool to build the modules against, to reduce + friction and have "out of the box" the ability to test across all the target + platforms. +- This goal of web-like code working via React Native on multiple platforms is + shared with Meta's + [RFC: React DOM for Native](https://github.com/react-native-community/discussions-and-proposals/pull/496), + which should be considered complementary to the proposal presented here ## Adoption strategy @@ -135,6 +158,20 @@ dependencies they need to add. At minimum we should: > this a breaking change? Can we write a codemod? Should we coordinate with > other projects or libraries? +In what we would call "phase zero", only one module will be implemented so that +the tooling and infrastructure can be build and flows can be set up. This is to +allow for multiple teams and developers to work discretely on the specific +modules that they are interested in, removing most of the friction of the +devloop to enable contributors to just focus on producing code. + +In "phase one", one or more modules will be adopted as experimental within a few +selected production apps to validate the concept and implementation. + +The "phase two" will be decided once we have as a good level of confidence on +the viability of this proposal. In this phase, documentation on how to use this +new set of modules will be prepared to help developers leverage them to bring +their Web code to other native platforms. + ## Unresolved questions - Which parts of the Web API do we prioritize first? From fed17d70c276f0b692644cd776cd98e59b9dcd68 Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Wed, 12 Jul 2023 15:15:26 +0100 Subject: [PATCH 04/22] some more tweaks, add mention to monorepo and infra --- text/0002-react-native-standard-api.md | 61 +++++++++++++++++++------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/text/0002-react-native-standard-api.md b/text/0002-react-native-standard-api.md index 5b099e749..e37ac29a4 100644 --- a/text/0002-react-native-standard-api.md +++ b/text/0002-react-native-standard-api.md @@ -5,14 +5,21 @@ ## Abstract -React Native currently lacks a well-defined, stable API. Today, we have a "wild -west" of community modules, each with its own set of interfaces and behaviors. -It is the developers' responsibility to find the module that fits their needs -and that is seemingly actively maintained. Additionally, the API are not -compatible with Web APIs, thus closing the door to a wealth of open source -libraries that do not have explicit React Native support. This often means that -developers cannot reuse existing web code, and must search for or even create -one for their needs. +React Native currently lacks a well-defined, stable and complete API surface. +When compared to what both +[iOS](https://developer.apple.com/documentation/technologies) and +[Android](https://developer.android.com/reference) provide out of the box, today +in React Native core quite a few are missing, and to fill that gap we have a +"wild west" of community modules, each with its own set of interfaces and +behaviors. While this is in line with the broader experience in the web/npm +space, at the end of the day this means that it is the developers' +responsibility to find modules that fits their needs and that are seemingly +actively maintained. + +Additionally, the API are not compatible with Web APIs, thus closing the door to +a wealth of open source libraries that do not have explicit React Native +support. This often means that developers cannot reuse existing web code, and +must search for or even create one for their needs. In this RFC, we are proposing to close this gap by providing our own implementation of the @@ -50,7 +57,7 @@ unused bits; nor does it make any sense to include MBs of dependencies that are never used. Ideally, migrating from community modules to the standard API should not increase the final app size (at least not significantly). -The API is implemented in layers: +The API we envision being implemented in layers: ```mermaid graph TD; @@ -83,9 +90,13 @@ graph TD; ### Modularity We want to avoid introducing unused modules and adding unnecessary bloat to the -app bundle. The API should therefore be broken down into smaller modules that -can be installed separately. These modules are installed by autolinking, and -must therefore be explicitly added to an app's `package.json`. +app bundle. The standard API should therefore be broken down into small modules +that can be installed separately. These modules are installed by autolinking, +and must therefore be explicitly added to an app's `package.json`. + +For example, if you want to use `BatteryManager` you should not need to import +the whole `rn-standard-api` node_module, but only its dedicated +`rn-standard-api/battery-manager` submodule. Additionally, we want to avoid requiring that users manually add polyfills for the modules they need. Instead, we propose that modules that implement a part of @@ -104,6 +115,22 @@ module.exports = { Polyfills are gathered and passed to Metro via [`serializer.getPolyfills`][]. +### Infrastructure and repository setup + +On top of what has been mentioned above, we are still investigating the right +approach for how the code for this effort should be created and organised. The +most likely approach will involve a monorepo (similar to +[`rnx-kit`](https://github.com/microsoft/rnx-kit)) where each module will be its +own dedicated package; in it, implementation for all the various native +platforms will be present (so, we won't have +`rn-standard-api/packages/battery-manager-ios` and +`rn-standard-api/packages/battery-manager-android` but only +`rn-standard-api/packages/battery-manager`). + +This will also allow for different people to work on different modules at the +same time, while having a coherent infra to rely on for testing, +package/versioning management, etc. + ### Discovery The number of modules is high. Finding which modules provide which part of Web @@ -129,9 +156,13 @@ dependencies they need to add. At minimum we should: > maintain the proposed changes. - existing React Native apps might need to be adapted to shift to this new - modules + modules. - a lot of the tooling needed for this effort to succeed as detailed above needs - to be created + to be created. +- the number of Web APIs is very high, so implementing each and every of them + for all the platforms will take a massive amount of time and funding - + realistically, we will select a subset of APIs to focus on, based on needs and + usage data. ## Rationale, alternatives, and prior art @@ -150,7 +181,7 @@ dependencies they need to add. At minimum we should: - This goal of web-like code working via React Native on multiple platforms is shared with Meta's [RFC: React DOM for Native](https://github.com/react-native-community/discussions-and-proposals/pull/496), - which should be considered complementary to the proposal presented here + which should be considered complementary to the proposal presented here. ## Adoption strategy From b706985546f2d42b2ea77c466a4647a3d9d04397 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Tue, 18 Jul 2023 14:01:17 +0200 Subject: [PATCH 05/22] tweaks --- text/0002-react-native-standard-api.md | 98 ++++++++++++-------------- 1 file changed, 46 insertions(+), 52 deletions(-) diff --git a/text/0002-react-native-standard-api.md b/text/0002-react-native-standard-api.md index e37ac29a4..2da2840e5 100644 --- a/text/0002-react-native-standard-api.md +++ b/text/0002-react-native-standard-api.md @@ -6,18 +6,17 @@ ## Abstract React Native currently lacks a well-defined, stable and complete API surface. -When compared to what both -[iOS](https://developer.apple.com/documentation/technologies) and -[Android](https://developer.android.com/reference) provide out of the box, today -in React Native core quite a few are missing, and to fill that gap we have a +Compared to what both [Android](https://developer.android.com/reference) and +[iOS](https://developer.apple.com/documentation/technologies) provide out of the +box, React Native core is missing quite a lot today. To fill that gap, we have a "wild west" of community modules, each with its own set of interfaces and behaviors. While this is in line with the broader experience in the web/npm space, at the end of the day this means that it is the developers' -responsibility to find modules that fits their needs and that are seemingly +responsibility to find modules that fit their needs and that are seemingly actively maintained. -Additionally, the API are not compatible with Web APIs, thus closing the door to -a wealth of open source libraries that do not have explicit React Native +Additionally, the APIs are not compatible with Web APIs, thus closing the door +to a wealth of open source libraries that do not have explicit React Native support. This often means that developers cannot reuse existing web code, and must search for or even create one for their needs. @@ -44,8 +43,8 @@ function useBatteryLevel() { } ``` -The native platforms we aim to support via react-native are iOS, Android, -Windows and macOS. +The native platforms we aim to support via react-native are Android, iOS, macOS, +Windows. ## Reference-level explanation @@ -57,7 +56,7 @@ unused bits; nor does it make any sense to include MBs of dependencies that are never used. Ideally, migrating from community modules to the standard API should not increase the final app size (at least not significantly). -The API we envision being implemented in layers: +The API we envision are implemented in the following layers: ```mermaid graph TD; @@ -66,7 +65,7 @@ graph TD; id2(Turbo/native module)-->id3(Platform API/\nCommunity module); ``` -- **Web APIs:** On the surface, there are little to no differences from normal +- **Web APIs:** On the surface, there are little to no differences from typical web code. - **JS shim:** This is a thin layer for marshalling web API calls to native @@ -81,8 +80,8 @@ graph TD; autolinking mechanism. - **Platform API/Community module:** When available, we want to be able to - leverage what already exist in the community and avoid creating competing - solutions. When we need to write it from scratch, we want to comply with the + leverage what already exists in the community and avoid creating competing + solutions. When we need to write from scratch, we want to comply with the guidelines for the [new architecture libraries](https://github.com/reactwg/react-native-new-architecture/discussions/categories/libraries) provided by Meta. @@ -90,13 +89,13 @@ graph TD; ### Modularity We want to avoid introducing unused modules and adding unnecessary bloat to the -app bundle. The standard API should therefore be broken down into small modules +app bundle. The standard API must therefore be broken down into smaller modules that can be installed separately. These modules are installed by autolinking, and must therefore be explicitly added to an app's `package.json`. For example, if you want to use `BatteryManager` you should not need to import -the whole `rn-standard-api` node_module, but only its dedicated -`rn-standard-api/battery-manager` submodule. +the whole `react-native-apis` module, but only the specific +`@react-native-apis/battery-manager` submodule. Additionally, we want to avoid requiring that users manually add polyfills for the modules they need. Instead, we propose that modules that implement a part of @@ -121,15 +120,14 @@ On top of what has been mentioned above, we are still investigating the right approach for how the code for this effort should be created and organised. The most likely approach will involve a monorepo (similar to [`rnx-kit`](https://github.com/microsoft/rnx-kit)) where each module will be its -own dedicated package; in it, implementation for all the various native -platforms will be present (so, we won't have -`rn-standard-api/packages/battery-manager-ios` and -`rn-standard-api/packages/battery-manager-android` but only -`rn-standard-api/packages/battery-manager`). +own dedicated package. For now, we will suggest that implementations for all +supported platforms be present in the one package. We won't have +`@react-native-apis/packages/battery-manager-ios` and +`@react-native-apis/packages/battery-manager-android`, only +`@react-native-apis/packages/battery-manager`. -This will also allow for different people to work on different modules at the -same time, while having a coherent infra to rely on for testing, -package/versioning management, etc. +This should allow for different people to work on different modules at the same +time, while having a coherent infra to rely on for testing, publishing, etc. ### Discovery @@ -138,11 +136,11 @@ APIs can be overwhelming for consumers. We need tools that can tell users which dependencies they need to add. At minimum we should: 1. Implement a tool for detecting usage of web APIs - - The tool should be able to list used web APIs and report on uses that have - not been polyfilled. + - The tool should be able to list used web APIs and flag uses that have not + been polyfilled. - If possible, the tool should also recommend which dependencies to add and/or automatically add it to `package.json`. - - Note: While we say "tool" here, it doesn't necessarily have to be a + - **Note:** While we say "tool" here, it doesn't necessarily have to be a standalone thing. A Babel plugin or similar would also fit. The less users have to worry about it, the better. 2. Add new capabilities to [`@rnx-kit/align-deps`][] @@ -151,18 +149,13 @@ dependencies they need to add. At minimum we should: ## Drawbacks -> TODO: Why should we not do this, and what is the impact of that decision? -> Please consider the cost to educate developers, migrate existing users, and -> maintain the proposed changes. - -- existing React Native apps might need to be adapted to shift to this new - modules. -- a lot of the tooling needed for this effort to succeed as detailed above needs +- Existing React Native apps might need to be adapted this new paradigm. +- A lot of the tooling needed for this effort to succeed as detailed above needs to be created. -- the number of Web APIs is very high, so implementing each and every of them - for all the platforms will take a massive amount of time and funding - +- The number of Web APIs is very high. Implementing each and every one of them + for all the platforms will take a massive amount of time and funding — realistically, we will select a subset of APIs to focus on, based on needs and - usage data. + real usage data. ## Rationale, alternatives, and prior art @@ -175,7 +168,7 @@ dependencies they need to add. At minimum we should: [`Object.assign`][], [`Object.is`][]). We have not found any that address the scope defined here. - [React Native Test App](https://github.com/microsoft/react-native-test-app) - will be used as the sandbox tool to build the modules against, to reduce + will be used as the sandbox tool to build the modules against to reduce friction and have "out of the box" the ability to test across all the target platforms. - This goal of web-like code working via React Native on multiple platforms is @@ -185,23 +178,24 @@ dependencies they need to add. At minimum we should: ## Adoption strategy -> TODO: If we accept this proposal, how will existing developers adopt it? Is -> this a breaking change? Can we write a codemod? Should we coordinate with -> other projects or libraries? +We will be following the crawl-walk-run methodology: -In what we would call "phase zero", only one module will be implemented so that -the tooling and infrastructure can be build and flows can be set up. This is to -allow for multiple teams and developers to work discretely on the specific -modules that they are interested in, removing most of the friction of the -devloop to enable contributors to just focus on producing code. +- **Crawl stage:** Only one module will be implemented so that the tooling and + infrastructure can be built and flows can be set up. We want to evaluate + whether things up to this point still make sense and adapt if otherwise. + Additionally, the infrastructure should allow for multiple teams and + developers to work discretely on the specific modules that they are interested + in, removing most of the friction to enable contributors to just focus on + producing code. -In "phase one", one or more modules will be adopted as experimental within a few -selected production apps to validate the concept and implementation. +- **Walk stage:** One or more modules will be adopted as experimental within a + few selected production apps to validate the concept and implementation. This + implies that we have worked out what modules to prioritize by this stage. -The "phase two" will be decided once we have as a good level of confidence on -the viability of this proposal. In this phase, documentation on how to use this -new set of modules will be prepared to help developers leverage them to bring -their Web code to other native platforms. +- **Run stage:** By this stage, we aim to have a good level of confidence on the + viability of this proposal. Documentation on how to use this new set of + modules will be prepared to help developers leverage them to bring their web + code to native platforms. ## Unresolved questions From 98b0ea29bf78e158fbe58e57f5d9e72121e813c7 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Tue, 18 Jul 2023 14:15:53 +0200 Subject: [PATCH 06/22] fixup! tweaks --- text/0002-react-native-standard-api.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/text/0002-react-native-standard-api.md b/text/0002-react-native-standard-api.md index 2da2840e5..356cc9c3c 100644 --- a/text/0002-react-native-standard-api.md +++ b/text/0002-react-native-standard-api.md @@ -94,7 +94,7 @@ that can be installed separately. These modules are installed by autolinking, and must therefore be explicitly added to an app's `package.json`. For example, if you want to use `BatteryManager` you should not need to import -the whole `react-native-apis` module, but only the specific +the whole `react-native-apis` module, but only the dedicated `@react-native-apis/battery-manager` submodule. Additionally, we want to avoid requiring that users manually add polyfills for @@ -122,9 +122,8 @@ most likely approach will involve a monorepo (similar to [`rnx-kit`](https://github.com/microsoft/rnx-kit)) where each module will be its own dedicated package. For now, we will suggest that implementations for all supported platforms be present in the one package. We won't have -`@react-native-apis/packages/battery-manager-ios` and -`@react-native-apis/packages/battery-manager-android`, only -`@react-native-apis/packages/battery-manager`. +`/packages/battery-manager-ios` and `/packages/battery-manager-android`, only +`/packages/battery-manager`. This should allow for different people to work on different modules at the same time, while having a coherent infra to rely on for testing, publishing, etc. From e23792d8b8c023d6f899afe948b347644c320d13 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Tue, 18 Jul 2023 14:29:28 +0200 Subject: [PATCH 07/22] fixup! tweaks --- text/0002-react-native-standard-api.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/text/0002-react-native-standard-api.md b/text/0002-react-native-standard-api.md index 356cc9c3c..f0e78cd6d 100644 --- a/text/0002-react-native-standard-api.md +++ b/text/0002-react-native-standard-api.md @@ -180,12 +180,11 @@ dependencies they need to add. At minimum we should: We will be following the crawl-walk-run methodology: - **Crawl stage:** Only one module will be implemented so that the tooling and - infrastructure can be built and flows can be set up. We want to evaluate - whether things up to this point still make sense and adapt if otherwise. - Additionally, the infrastructure should allow for multiple teams and - developers to work discretely on the specific modules that they are interested - in, removing most of the friction to enable contributors to just focus on - producing code. + infrastructure can be built and flows can be set up. The infrastructure should + allow for multiple teams and developers to work discretely on the specific + modules that they are interested in, removing most of the friction to enable + contributors to just focus on producing code. We want to evaluate whether + things up to this point still make sense and adapt if otherwise. - **Walk stage:** One or more modules will be adopted as experimental within a few selected production apps to validate the concept and implementation. This From 2a042153a609c8f2f1ae84eb45f69bfdd2057c24 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Wed, 2 Aug 2023 16:05:55 +0200 Subject: [PATCH 08/22] allow additional platform implementations --- text/0002-react-native-standard-api.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/text/0002-react-native-standard-api.md b/text/0002-react-native-standard-api.md index f0e78cd6d..1aa41199c 100644 --- a/text/0002-react-native-standard-api.md +++ b/text/0002-react-native-standard-api.md @@ -94,8 +94,8 @@ that can be installed separately. These modules are installed by autolinking, and must therefore be explicitly added to an app's `package.json`. For example, if you want to use `BatteryManager` you should not need to import -the whole `react-native-apis` module, but only the dedicated -`@react-native-apis/battery-manager` submodule. +the whole `react-native-webapis` module, but only the dedicated +`@react-native-webapis/battery-manager` submodule. Additionally, we want to avoid requiring that users manually add polyfills for the modules they need. Instead, we propose that modules that implement a part of @@ -120,10 +120,13 @@ On top of what has been mentioned above, we are still investigating the right approach for how the code for this effort should be created and organised. The most likely approach will involve a monorepo (similar to [`rnx-kit`](https://github.com/microsoft/rnx-kit)) where each module will be its -own dedicated package. For now, we will suggest that implementations for all -supported platforms be present in the one package. We won't have -`/packages/battery-manager-ios` and `/packages/battery-manager-android`, only -`/packages/battery-manager`. +own dedicated package. For starters, we will suggest that implementations for +"core" supported platforms (i.e. Android, iOS, macOS, Windows) be present in the +one package — we won't have `/~/battery-manager-android` and +`/~/battery-manager-ios`, only `/~/battery-manager`. However, it should still be +possible to have additional platform specific implementations, e.g. +`/~/battery-manager-tvos`. These should also be treated as first class citizens +and be recognized by all tooling. This should allow for different people to work on different modules at the same time, while having a coherent infra to rely on for testing, publishing, etc. From 9bf372bbd7ccdbcf829cf7b2ee61098656041d92 Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Thu, 3 Aug 2023 11:08:44 +0100 Subject: [PATCH 09/22] change the name from standard to webapi --- ...e-standard-api.md => 0002-react-native-webapi.md} | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename text/{0002-react-native-standard-api.md => 0002-react-native-webapi.md} (96%) diff --git a/text/0002-react-native-standard-api.md b/text/0002-react-native-webapi.md similarity index 96% rename from text/0002-react-native-standard-api.md rename to text/0002-react-native-webapi.md index 1aa41199c..9a3782cb7 100644 --- a/text/0002-react-native-standard-api.md +++ b/text/0002-react-native-webapi.md @@ -1,4 +1,4 @@ -- Title: react-native-standard-api +- Title: react-native-webapi - Date: 2023-07-04 - RFC PR: https://github.com/microsoft/rnx-kit/pull/2504 - rnx-kit Issue: N/A @@ -53,8 +53,8 @@ Our goal is not to reimplement a browser, e.g. a full browser with everything that entails, including modules that they may never get used. Native apps, and mobile apps especially, cannot be shipped with unused bits; nor does it make any sense to include MBs of dependencies that are -never used. Ideally, migrating from community modules to the standard API should -not increase the final app size (at least not significantly). +never used. Ideally, migrating from community modules to the WebAPI should not +increase the final app size (at least not significantly). The API we envision are implemented in the following layers: @@ -89,9 +89,9 @@ graph TD; ### Modularity We want to avoid introducing unused modules and adding unnecessary bloat to the -app bundle. The standard API must therefore be broken down into smaller modules -that can be installed separately. These modules are installed by autolinking, -and must therefore be explicitly added to an app's `package.json`. +app bundle. The WebAPIs must therefore be broken down into smaller modules that +can be installed separately. These modules are installed by autolinking, and +must therefore be explicitly added to an app's `package.json`. For example, if you want to use `BatteryManager` you should not need to import the whole `react-native-webapis` module, but only the dedicated From cb9f80638aa8bda6e8f773bb1539e3cbc41ad0a8 Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Thu, 3 Aug 2023 11:10:38 +0100 Subject: [PATCH 10/22] reflect proper npm org name --- text/0002-react-native-webapi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0002-react-native-webapi.md b/text/0002-react-native-webapi.md index 9a3782cb7..7e18b7e8d 100644 --- a/text/0002-react-native-webapi.md +++ b/text/0002-react-native-webapi.md @@ -164,7 +164,7 @@ dependencies they need to add. At minimum we should: - A variation of the current proposal without polyfills was considered, but it would require users to change web code to accommodate native. For instance, `navigator.getBattery()` would have to be rewritten as - `require("@react-native-api/battery-manager").getBattery()`. + `require("@react-native-webapis/battery-manager").getBattery()`. - There are many polyfills out there, but they are mostly used to provide functionalities that are only present in newer ES standards (e.g. [`Object.assign`][], [`Object.is`][]). We have not found any that address the From 8280738a989131ece49913fda6c41e7c6532a198 Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Thu, 3 Aug 2023 11:14:51 +0100 Subject: [PATCH 11/22] Update text/0002-react-native-webapi.md Co-authored-by: Tommy Nguyen <4123478+tido64@users.noreply.github.com> --- text/0002-react-native-webapi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0002-react-native-webapi.md b/text/0002-react-native-webapi.md index 7e18b7e8d..d811603d8 100644 --- a/text/0002-react-native-webapi.md +++ b/text/0002-react-native-webapi.md @@ -1,4 +1,4 @@ -- Title: react-native-webapi +- Title: react-native-webapis - Date: 2023-07-04 - RFC PR: https://github.com/microsoft/rnx-kit/pull/2504 - rnx-kit Issue: N/A From 5455b97168109ba36e94d77885e98a53fd90654f Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Thu, 3 Aug 2023 11:14:57 +0100 Subject: [PATCH 12/22] Update text/0002-react-native-webapi.md Co-authored-by: Tommy Nguyen <4123478+tido64@users.noreply.github.com> --- text/0002-react-native-webapi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0002-react-native-webapi.md b/text/0002-react-native-webapi.md index d811603d8..eb19a51c6 100644 --- a/text/0002-react-native-webapi.md +++ b/text/0002-react-native-webapi.md @@ -53,7 +53,7 @@ Our goal is not to reimplement a browser, e.g. a full browser with everything that entails, including modules that they may never get used. Native apps, and mobile apps especially, cannot be shipped with unused bits; nor does it make any sense to include MBs of dependencies that are -never used. Ideally, migrating from community modules to the WebAPI should not +never used. Ideally, migrating from community modules to the WebAPIs should not increase the final app size (at least not significantly). The API we envision are implemented in the following layers: From 4508f3adb45843ef63e237e251999569109d203f Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Thu, 3 Aug 2023 11:15:02 +0100 Subject: [PATCH 13/22] Update text/0002-react-native-webapi.md Co-authored-by: Tommy Nguyen <4123478+tido64@users.noreply.github.com> --- text/0002-react-native-webapi.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0002-react-native-webapi.md b/text/0002-react-native-webapi.md index eb19a51c6..042c40619 100644 --- a/text/0002-react-native-webapi.md +++ b/text/0002-react-native-webapi.md @@ -89,9 +89,9 @@ graph TD; ### Modularity We want to avoid introducing unused modules and adding unnecessary bloat to the -app bundle. The WebAPIs must therefore be broken down into smaller modules that -can be installed separately. These modules are installed by autolinking, and -must therefore be explicitly added to an app's `package.json`. +app bundle. WebAPIs must therefore be broken down into smaller modules that can +be installed separately. These modules are installed by autolinking, and must +therefore be explicitly added to an app's `package.json`. For example, if you want to use `BatteryManager` you should not need to import the whole `react-native-webapis` module, but only the dedicated From 8409c71a0e4f3550ac22fc11930d16513a7f533b Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Thu, 3 Aug 2023 12:15:35 +0200 Subject: [PATCH 14/22] Rename 0002-react-native-webapi.md to 0002-react-native-webapis.md --- .../{0002-react-native-webapi.md => 0002-react-native-webapis.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename text/{0002-react-native-webapi.md => 0002-react-native-webapis.md} (100%) diff --git a/text/0002-react-native-webapi.md b/text/0002-react-native-webapis.md similarity index 100% rename from text/0002-react-native-webapi.md rename to text/0002-react-native-webapis.md From 0567637df0a78cbc3e64e4337c06959554e1c33c Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Thu, 3 Aug 2023 12:16:20 +0200 Subject: [PATCH 15/22] Update 0002-react-native-webapis.md --- text/0002-react-native-webapis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0002-react-native-webapis.md b/text/0002-react-native-webapis.md index 042c40619..ddca5b217 100644 --- a/text/0002-react-native-webapis.md +++ b/text/0002-react-native-webapis.md @@ -53,7 +53,7 @@ Our goal is not to reimplement a browser, e.g. a full browser with everything that entails, including modules that they may never get used. Native apps, and mobile apps especially, cannot be shipped with unused bits; nor does it make any sense to include MBs of dependencies that are -never used. Ideally, migrating from community modules to the WebAPIs should not +never used. Ideally, migrating from community modules to WebAPIs should not increase the final app size (at least not significantly). The API we envision are implemented in the following layers: From 1c32d86e19908f251c7225d04be93513b77d5de3 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Tue, 8 Aug 2023 16:06:17 +0200 Subject: [PATCH 16/22] links to prototypes --- text/0002-react-native-webapis.md | 33 ++++++++++++++++++------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/text/0002-react-native-webapis.md b/text/0002-react-native-webapis.md index ddca5b217..c65f70983 100644 --- a/text/0002-react-native-webapis.md +++ b/text/0002-react-native-webapis.md @@ -93,9 +93,9 @@ app bundle. WebAPIs must therefore be broken down into smaller modules that can be installed separately. These modules are installed by autolinking, and must therefore be explicitly added to an app's `package.json`. -For example, if you want to use `BatteryManager` you should not need to import +For example, if you want to use `BatteryStatus` you should not need to import the whole `react-native-webapis` module, but only the dedicated -`@react-native-webapis/battery-manager` submodule. +`@react-native-webapis/battery-status` submodule. Additionally, we want to avoid requiring that users manually add polyfills for the modules they need. Instead, we propose that modules that implement a part of @@ -112,7 +112,9 @@ module.exports = { }; ``` -Polyfills are gathered and passed to Metro via [`serializer.getPolyfills`][]. +Polyfills are gathered and passed to Metro. Any dependency with a correctly +declared polyfill will be included. They do not need to be under the +`@react-native-webapis` scope or even live in the same repository. ### Infrastructure and repository setup @@ -122,10 +124,10 @@ most likely approach will involve a monorepo (similar to [`rnx-kit`](https://github.com/microsoft/rnx-kit)) where each module will be its own dedicated package. For starters, we will suggest that implementations for "core" supported platforms (i.e. Android, iOS, macOS, Windows) be present in the -one package — we won't have `/~/battery-manager-android` and -`/~/battery-manager-ios`, only `/~/battery-manager`. However, it should still be +one package — we won't have `/~/battery-status-android` and +`/~/battery-status-ios`, only `/~/battery-status`. However, it should still be possible to have additional platform specific implementations, e.g. -`/~/battery-manager-tvos`. These should also be treated as first class citizens +`/~/battery-status-tvos`. These should also be treated as first class citizens and be recognized by all tooling. This should allow for different people to work on different modules at the same @@ -164,7 +166,7 @@ dependencies they need to add. At minimum we should: - A variation of the current proposal without polyfills was considered, but it would require users to change web code to accommodate native. For instance, `navigator.getBattery()` would have to be rewritten as - `require("@react-native-webapis/battery-manager").getBattery()`. + `require("@react-native-webapis/battery-status").getBattery()`. - There are many polyfills out there, but they are mostly used to provide functionalities that are only present in newer ES standards (e.g. [`Object.assign`][], [`Object.is`][]). We have not found any that address the @@ -201,13 +203,16 @@ We will be following the crawl-walk-run methodology: ## Unresolved questions - Which parts of the Web API do we prioritize first? - - As proof-of-concept, we suggest implementing - [`BatteryManager`](https://developer.mozilla.org/en-US/docs/Web/API/BatteryManager) + - As proof-of-concept, we've implemented the + [`Battery Status API`](https://developer.mozilla.org/en-US/docs/Web/API/Battery_Status_API) as it is small and self-contained. - - Android: - https://developer.android.com/training/monitoring-device-state/battery-monitoring - - iOS: - https://developer.apple.com/documentation/uikit/uidevice/1620051-batterystate + - https://github.com/microsoft/rnx-kit/pull/2590 + - Note that we're currently using + [`serializer.getModulesRunBeforeMainModule`][] until something better + exists. It currently requires that listed modules are explicitly imported + in the bundle itself, which we do not want. We need something akin to + Webpack's [entry points](https://webpack.js.org/concepts/entry-points/). + More details here: https://github.com/facebook/metro/issues/850. @@ -215,7 +220,7 @@ We will be following the crawl-walk-run methodology: https://github.com/microsoft/rnx-kit/tree/main/packages/align-deps#readme [`Object.assign`]: https://github.com/ljharb/object.assign/blob/main/polyfill.js [`Object.is`]: https://github.com/es-shims/object-is/blob/main/polyfill.js -[`serializer.getPolyfills`]: +[`serializer.getModulesRunBeforeMainModule`]: https://github.com/facebook/react-native/blob/0.72-stable/packages/metro-config/index.js#L49 [autolinking]: https://github.com/react-native-community/cli/blob/main/docs/autolinking.md From 714568bbee09cfd5685ee6d77b89ba449692e0cb Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Wed, 9 Aug 2023 14:25:15 +0200 Subject: [PATCH 17/22] mention Babel plugin --- text/0002-react-native-webapis.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/text/0002-react-native-webapis.md b/text/0002-react-native-webapis.md index c65f70983..ae31dab3f 100644 --- a/text/0002-react-native-webapis.md +++ b/text/0002-react-native-webapis.md @@ -209,10 +209,11 @@ We will be following the crawl-walk-run methodology: - https://github.com/microsoft/rnx-kit/pull/2590 - Note that we're currently using [`serializer.getModulesRunBeforeMainModule`][] until something better - exists. It currently requires that listed modules are explicitly imported - in the bundle itself, which we do not want. We need something akin to - Webpack's [entry points](https://webpack.js.org/concepts/entry-points/). - More details here: https://github.com/facebook/metro/issues/850. + exists. It requires that listed modules are explicitly imported in the + bundle itself, which we do not want, but are working around with a Babel + plugin. We need something akin to Webpack's + [entry points](https://webpack.js.org/concepts/entry-points/). More + details here: https://github.com/facebook/metro/issues/850. From 7baefbca779132b225ed8984dc0562ff4717fc87 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Wed, 9 Aug 2023 14:39:39 +0200 Subject: [PATCH 18/22] clarify how the plugin is used --- text/0002-react-native-webapis.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/text/0002-react-native-webapis.md b/text/0002-react-native-webapis.md index ae31dab3f..cabdfcc86 100644 --- a/text/0002-react-native-webapis.md +++ b/text/0002-react-native-webapis.md @@ -207,11 +207,9 @@ We will be following the crawl-walk-run methodology: [`Battery Status API`](https://developer.mozilla.org/en-US/docs/Web/API/Battery_Status_API) as it is small and self-contained. - https://github.com/microsoft/rnx-kit/pull/2590 - - Note that we're currently using - [`serializer.getModulesRunBeforeMainModule`][] until something better - exists. It requires that listed modules are explicitly imported in the - bundle itself, which we do not want, but are working around with a Babel - plugin. We need something akin to Webpack's + - We're currently using a Babel plugin and rely on a magic comment in the + bundle to generate import statements. To further automate this, we would + need something akin to Webpack's [entry points](https://webpack.js.org/concepts/entry-points/). More details here: https://github.com/facebook/metro/issues/850. From 18a3e1d20dd84b68cb77978c40ee9c4b6929db28 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Wed, 9 Aug 2023 15:30:21 +0200 Subject: [PATCH 19/22] Battery Status API is deprecated --- text/0002-react-native-webapis.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/0002-react-native-webapis.md b/text/0002-react-native-webapis.md index cabdfcc86..5f58c8b38 100644 --- a/text/0002-react-native-webapis.md +++ b/text/0002-react-native-webapis.md @@ -212,6 +212,8 @@ We will be following the crawl-walk-run methodology: need something akin to Webpack's [entry points](https://webpack.js.org/concepts/entry-points/). More details here: https://github.com/facebook/metro/issues/850. + - It looks like this API is deprecated due to privacy concerns: + https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/236 From 5684aba64817f0479295a6b086d5741144c0c08e Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Thu, 24 Aug 2023 08:27:07 +0200 Subject: [PATCH 20/22] feedback --- text/0002-react-native-webapis.md | 137 ++++++++++++++++-------------- 1 file changed, 75 insertions(+), 62 deletions(-) diff --git a/text/0002-react-native-webapis.md b/text/0002-react-native-webapis.md index 5f58c8b38..298d9f049 100644 --- a/text/0002-react-native-webapis.md +++ b/text/0002-react-native-webapis.md @@ -6,14 +6,12 @@ ## Abstract React Native currently lacks a well-defined, stable and complete API surface. -Compared to what both [Android](https://developer.android.com/reference) and -[iOS](https://developer.apple.com/documentation/technologies) provide out of the -box, React Native core is missing quite a lot today. To fill that gap, we have a -"wild west" of community modules, each with its own set of interfaces and -behaviors. While this is in line with the broader experience in the web/npm -space, at the end of the day this means that it is the developers' -responsibility to find modules that fit their needs and that are seemingly -actively maintained. +Compared to what both [Android][] and [iOS][] provide out of the box, React +Native core is missing quite a lot today. To fill that gap, we have a "wild +west" of community modules, each with its own set of interfaces and behaviors. +While this is in line with the broader experience in the web/npm space, at the +end of the day this means that it is the developers' responsibility to find +modules that fit their needs and that are seemingly actively maintained. Additionally, the APIs are not compatible with Web APIs, thus closing the door to a wealth of open source libraries that do not have explicit React Native @@ -21,11 +19,10 @@ support. This often means that developers cannot reuse existing web code, and must search for or even create one for their needs. In this RFC, we are proposing to close this gap by providing our own -implementation of the -[Web APIs](https://developer.mozilla.org/en-US/docs/Web/API) for React Native. -The ultimate goal is to open the possibility to run non-UI code directly in a -React Native app; to provide a familiar environment for existing web developers -as well as a well-documented API for developers of any experience level. +implementation of the [Web APIs][] for React Native. The ultimate goal is to +open the possibility to run non-UI code directly in a React Native app; to +provide a familiar environment for existing web developers as well as a +well-documented API for developers of any experience level. ## Guide-level explanation @@ -48,43 +45,48 @@ Windows. ## Reference-level explanation -Our goal is not to reimplement a browser, e.g. -[Electron](https://www.electronjs.org/). Apps made this way currently ship with -a full browser with everything that entails, including modules that they may -never get used. Native apps, and mobile apps especially, cannot be shipped with -unused bits; nor does it make any sense to include MBs of dependencies that are -never used. Ideally, migrating from community modules to WebAPIs should not -increase the final app size (at least not significantly). +Our goal is not to reimplement a browser, e.g. [Electron][]. Apps made this way +currently ship with a full browser with everything that entails, including +modules that they may never get used. Native apps, and mobile apps especially, +cannot be shipped with unused bits; nor does it make any sense to include MBs of +dependencies that are never used. Ideally, migrating from community modules to +WebAPIs should not increase the final app size (at least not significantly). -The API we envision are implemented in the following layers: +We envision the APIs can be implemented in at least two ways: ```mermaid graph TD; id(Web APIs)-->id1(JS shim); id1(JS shim)-->id2(Turbo/native module); id2(Turbo/native module)-->id3(Platform API/\nCommunity module); + id(Web APIs)-->id10(JS implementation); ``` - **Web APIs:** On the surface, there are little to no differences from typical web code. -- **JS shim:** This is a thin layer for marshalling web API calls to native - modules. It needs to be installed before it can be used. The current thinking - is to install this layer as polyfills. Installing them on-demand is preferred, - but we need to start somewhere and we should be able to improve this layer - later without affecting users. In any case, we will need tools to include - polyfills similarly to [autolinking][]. - -- **Turbo/native module:** This is the layer that accepts JS calls and forwards - them platform implementations. Modules are installed via the standard - autolinking mechanism. - -- **Platform API/Community module:** When available, we want to be able to - leverage what already exists in the community and avoid creating competing - solutions. When we need to write from scratch, we want to comply with the - guidelines for the - [new architecture libraries](https://github.com/reactwg/react-native-new-architecture/discussions/categories/libraries) - provided by Meta. +- The APIs can be implemented as a native module and a shim layer: + + - **JS shim:** This is a thin layer for marshalling web API calls to native + modules. It needs to be installed before it can be used. The current + thinking is to install this layer as polyfills. Installing them on-demand is + preferred, but we need to start somewhere and we should be able to improve + this layer later without affecting users. In any case, we will need tools to + include polyfills similarly to [autolinking][]. + - **Turbo/native module:** This is the layer that accepts JS calls and + forwards them platform implementations. Modules are installed via the + standard autolinking mechanism. + - Note: Some Web APIs may necessitate JSI bindings e.g., to properly manage + native resources or because the object model in the API lends itself more + towards live, stateful, graphs of objects which would otherwise require a + thicker polyfill. + - **Platform API/Community module:** When available, we want to be able to + leverage what already exists in the community and avoid creating competing + solutions. When we need to write from scratch, we want to comply with the + guidelines for the [new architecture libraries][] provided by Meta. + +- Alternatively, native code may not be necessary at all if a complete + implementation can be done in pure JS. ### Modularity @@ -120,15 +122,14 @@ declared polyfill will be included. They do not need to be under the On top of what has been mentioned above, we are still investigating the right approach for how the code for this effort should be created and organised. The -most likely approach will involve a monorepo (similar to -[`rnx-kit`](https://github.com/microsoft/rnx-kit)) where each module will be its -own dedicated package. For starters, we will suggest that implementations for -"core" supported platforms (i.e. Android, iOS, macOS, Windows) be present in the -one package — we won't have `/~/battery-status-android` and -`/~/battery-status-ios`, only `/~/battery-status`. However, it should still be -possible to have additional platform specific implementations, e.g. -`/~/battery-status-tvos`. These should also be treated as first class citizens -and be recognized by all tooling. +most likely approach will involve a monorepo (similar to [`rnx-kit`][]) where +each module will be its own dedicated package. For starters, we will suggest +that implementations for "core" supported platforms (i.e. Android, iOS, macOS, +Windows) be present in the one package — we won't have +`/~/battery-status-android` and `/~/battery-status-ios`, only +`/~/battery-status`. However, it should still be possible to have additional +platform specific implementations, e.g. `/~/battery-status-tvos`. These should +also be treated as first class citizens and be recognized by all tooling. This should allow for different people to work on different modules at the same time, while having a coherent infra to rely on for testing, publishing, etc. @@ -167,18 +168,19 @@ dependencies they need to add. At minimum we should: would require users to change web code to accommodate native. For instance, `navigator.getBattery()` would have to be rewritten as `require("@react-native-webapis/battery-status").getBattery()`. + - On the other hand, this approach has the advantage of not polluting the + global space with typings for APIs that may not be available in your + project. - There are many polyfills out there, but they are mostly used to provide functionalities that are only present in newer ES standards (e.g. [`Object.assign`][], [`Object.is`][]). We have not found any that address the scope defined here. -- [React Native Test App](https://github.com/microsoft/react-native-test-app) - will be used as the sandbox tool to build the modules against to reduce - friction and have "out of the box" the ability to test across all the target - platforms. +- [React Native Test App][] will be used as the sandbox tool to build the + modules against to reduce friction and have "out of the box" the ability to + test across all the target platforms. - This goal of web-like code working via React Native on multiple platforms is - shared with Meta's - [RFC: React DOM for Native](https://github.com/react-native-community/discussions-and-proposals/pull/496), - which should be considered complementary to the proposal presented here. + shared with Meta's [RFC: React DOM for Native][], which should be considered + complementary to the proposal presented here. ## Adoption strategy @@ -203,15 +205,13 @@ We will be following the crawl-walk-run methodology: ## Unresolved questions - Which parts of the Web API do we prioritize first? - - As proof-of-concept, we've implemented the - [`Battery Status API`](https://developer.mozilla.org/en-US/docs/Web/API/Battery_Status_API) - as it is small and self-contained. - - https://github.com/microsoft/rnx-kit/pull/2590 + - As proof-of-concept, we've implemented the [Battery Status API][] as it is + small and self-contained. + - Code: https://github.com/microsoft/rnx-kit/pull/2590 - We're currently using a Babel plugin and rely on a magic comment in the - bundle to generate import statements. To further automate this, we would - need something akin to Webpack's - [entry points](https://webpack.js.org/concepts/entry-points/). More - details here: https://github.com/facebook/metro/issues/850. + bundle to generate import statements. An alternative implementation would + be to use something akin to Webpack's [entry points][]. More details here: + https://github.com/facebook/metro/issues/850. - It looks like this API is deprecated due to privacy concerns: https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/236 @@ -221,7 +221,20 @@ We will be following the crawl-walk-run methodology: https://github.com/microsoft/rnx-kit/tree/main/packages/align-deps#readme [`Object.assign`]: https://github.com/ljharb/object.assign/blob/main/polyfill.js [`Object.is`]: https://github.com/es-shims/object-is/blob/main/polyfill.js +[`rnx-kit`]: https://github.com/microsoft/rnx-kit [`serializer.getModulesRunBeforeMainModule`]: https://github.com/facebook/react-native/blob/0.72-stable/packages/metro-config/index.js#L49 +[Android]: https://developer.android.com/reference +[Battery Status API]: + https://developer.mozilla.org/en-US/docs/Web/API/Battery_Status_API +[Electron]: https://www.electronjs.org/ +[RFC: React DOM for Native]: + https://github.com/react-native-community/discussions-and-proposals/pull/496 +[React Native Test App]: https://github.com/microsoft/react-native-test-app +[Web APIs]: https://developer.mozilla.org/en-US/docs/Web/API [autolinking]: https://github.com/react-native-community/cli/blob/main/docs/autolinking.md +[entry points]: https://webpack.js.org/concepts/entry-points/ +[iOS]: https://developer.apple.com/documentation/technologies +[new architecture libraries]: + https://github.com/reactwg/react-native-new-architecture/discussions/categories/libraries From cca0e1ebd3b79ec7ee886a57efa26a3bd555c6e8 Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Fri, 1 Sep 2023 15:25:08 +0100 Subject: [PATCH 21/22] expand and update the RFC with new information --- text/0002-react-native-webapis.md | 90 ++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/text/0002-react-native-webapis.md b/text/0002-react-native-webapis.md index 298d9f049..8c2653ab7 100644 --- a/text/0002-react-native-webapis.md +++ b/text/0002-react-native-webapis.md @@ -74,7 +74,7 @@ graph TD; this layer later without affecting users. In any case, we will need tools to include polyfills similarly to [autolinking][]. - **Turbo/native module:** This is the layer that accepts JS calls and - forwards them platform implementations. Modules are installed via the + forwards them to platform implementations. Modules are installed via the standard autolinking mechanism. - Note: Some Web APIs may necessitate JSI bindings e.g., to properly manage native resources or because the object model in the API lends itself more @@ -123,10 +123,11 @@ declared polyfill will be included. They do not need to be under the On top of what has been mentioned above, we are still investigating the right approach for how the code for this effort should be created and organised. The most likely approach will involve a monorepo (similar to [`rnx-kit`][]) where -each module will be its own dedicated package. For starters, we will suggest -that implementations for "core" supported platforms (i.e. Android, iOS, macOS, -Windows) be present in the one package — we won't have -`/~/battery-status-android` and `/~/battery-status-ios`, only +each module will be its own dedicated package. + +For starters, we will suggest that implementations for "core" supported +platforms (i.e. Android, iOS, macOS, Windows) be present in the one package — we +won't have `/~/battery-status-android` and `/~/battery-status-ios`, only `/~/battery-status`. However, it should still be possible to have additional platform specific implementations, e.g. `/~/battery-status-tvos`. These should also be treated as first class citizens and be recognized by all tooling. @@ -134,6 +135,42 @@ also be treated as first class citizens and be recognized by all tooling. This should allow for different people to work on different modules at the same time, while having a coherent infra to rely on for testing, publishing, etc. +During the initial phase of this effort we should keep all the packages marked +as private so that they won't be published to npm. This should allow for more +flexibility in starting to implement code while the details of the final setup +get agreed on. + +#### Testing + +Ideally we want to rely on the already existing test suites that W3C +[provides for QA](https://www.w3.org/QA/Test/). Here's an example for the +[Geolocation](https://dev.w3.org/geo/api/test-suite/) webapi, and a +[single test](https://dev.w3.org/geo/api/test-suite/t00001.js). + +### Prioritization strategy + +The number of Web APIs is very high (1000+). Implementing each and every one of +them for all the platforms will take a massive amount of time and funding — +realistically, we need a strategy to decide how to select and prioritize which +APIs to work on. + +As part of our investigation, we have created +[a script](https://github.com/microsoft/rnx-kit/tree/main/incubator/%40react-native-webapis/types) +that extracts the interfaces from the TS typings for webapis (from +`https://unpkg.com/@mdn/browser-compat-data/data.json`) and allows us to filter +them, removing the ones we know for sure we don't want to implement +([ex. DOM, HTML, etc](https://github.com/microsoft/rnx-kit/blob/main/incubator/%40react-native-webapis/types/generate.mjs#L10)). +That still leaves us with around 200 APIs, still way too many. + +To pair with the script we have been working on a +[tool to scan](https://github.com/microsoft/rnx-kit/pull/2621) the usage of +webapis across a codebase. This allows us to verify which APIs are relevant and +can get higher priority. + +This is where the need for a monorepo with a quick devloop comes in handy: so +that partners/interested companies can volunteer to take ownership of some +specific webapis and work on them in parallel, in a coordinate manner. + ### Discovery The number of modules is high. Finding which modules provide which part of Web @@ -152,15 +189,37 @@ dependencies they need to add. At minimum we should: - `align-deps` ensures that package versions are aligned and can help keeping track of transitive dependencies. +## An example implementation + +As proof-of-concept, we've implemented the [Battery Status API][] as it is small +and self-contained. The code can be +[found here](https://github.com/microsoft/rnx-kit/pull/2590). + +To get it to work, we had to use a custom plugin +([`@rnx-kit/polyfills`](https://github.com/microsoft/rnx-kit/tree/main/incubator/polyfills)) +and rely on a "magic comment" in the bundle to generate import statements. An +alternative implementation would be to use something akin to Webpack's [entry +points][]. More details here: https://github.com/facebook/metro/issues/850. + ## Drawbacks - Existing React Native apps might need to be adapted this new paradigm. - A lot of the tooling needed for this effort to succeed as detailed above needs to be created. -- The number of Web APIs is very high. Implementing each and every one of them - for all the platforms will take a massive amount of time and funding — - realistically, we will select a subset of APIs to focus on, based on needs and - real usage data. +- The number of Web APIs is very high (1000+). See the section above about + prioritization strategy for more details on how we are planning to tackle + this. +- It looks like it's very hard to properly detected how ownership about an API + endpoint works, tracking usage and if it's viable or not. For example, it's + kind of surprising to find that the only clear reference about the Battery + Status API being deprecated is + [in a comment in a PR in a TS repo](https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/236). + The official documentation doesn't seem to reflect actual status that well + https://w3c.github.io/battery/#the-batterymanager-interface + - Our current idea would be to leverage the existence or not of said API in + the TS interface (which is also what we use for our script for generating + the "valid interfaces" mentioned above), using it as "realistic source of + truth" ## Rationale, alternatives, and prior art @@ -204,16 +263,9 @@ We will be following the crawl-walk-run methodology: ## Unresolved questions -- Which parts of the Web API do we prioritize first? - - As proof-of-concept, we've implemented the [Battery Status API][] as it is - small and self-contained. - - Code: https://github.com/microsoft/rnx-kit/pull/2590 - - We're currently using a Babel plugin and rely on a magic comment in the - bundle to generate import statements. An alternative implementation would - be to use something akin to Webpack's [entry points][]. More details here: - https://github.com/facebook/metro/issues/850. - - It looks like this API is deprecated due to privacy concerns: - https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/236 +- How do we keep track of which WebAPIs are at which stage of their + support/implementation on the web side, so that we don't want on + unused/deprecated APIs? From df23ecb2fc6b8bc7129ea06de26bdeb6868b5c2a Mon Sep 17 00:00:00 2001 From: Lorenzo Sciandra Date: Tue, 26 Sep 2023 15:10:51 +0100 Subject: [PATCH 22/22] small addition to RFC --- text/0002-react-native-webapis.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/text/0002-react-native-webapis.md b/text/0002-react-native-webapis.md index 8c2653ab7..e1a1db7a9 100644 --- a/text/0002-react-native-webapis.md +++ b/text/0002-react-native-webapis.md @@ -201,6 +201,14 @@ and rely on a "magic comment" in the bundle to generate import statements. An alternative implementation would be to use something akin to Webpack's [entry points][]. More details here: https://github.com/facebook/metro/issues/850. +This implementation allowed us to establish some common pattern to be reused in +future web APIs implementations such as the subfolders division and the +convention native-side of using `RNW` as module name. On top of +this we will be preparing a shallow template to allow a quicker generation of +new web APIs that will follow the existing DX in rnx-kit of using +`yarn new-package ` as +[detailed here](https://github.com/microsoft/rnx-kit/blob/main/CONTRIBUTING.md#adding-a-new-package). + ## Drawbacks - Existing React Native apps might need to be adapted this new paradigm. @@ -266,6 +274,13 @@ We will be following the crawl-walk-run methodology: - How do we keep track of which WebAPIs are at which stage of their support/implementation on the web side, so that we don't want on unused/deprecated APIs? +- How do we want to track and coordinate work on each web API? An idea could be + to have an umbrella issue in the `rnx-kit` repository that serves as an + overall tracker, indicating (only) which web APIs are being worked on, who is + working on them, and status of the implementation (statuses to be defined). +- If the overall idea is to get web and native code to be used the same way, + what bundler should then be used in the web app and which in the native app? + Should it be the same? Further investigations ongoing.