From 56fcb1fbbe59f0d2547d823729d5b2afef686490 Mon Sep 17 00:00:00 2001 From: bacarybruno Date: Sun, 19 Oct 2025 23:24:42 +0200 Subject: [PATCH 1/4] docs: strip flow types --- ...strip-flow-types-from-published-package.md | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 proposals/0000-strip-flow-types-from-published-package.md diff --git a/proposals/0000-strip-flow-types-from-published-package.md b/proposals/0000-strip-flow-types-from-published-package.md new file mode 100644 index 0000000..c29fc39 --- /dev/null +++ b/proposals/0000-strip-flow-types-from-published-package.md @@ -0,0 +1,163 @@ +--- +title: Strip Flow Types from React Native Published Package +author: +- Bruno Bodian +date: 2025-10-19 +--- + +# RFC0000: Strip Flow Types from React Native Published Package + +## Summary + +This proposal seeks to remove Flow type annotations from the JavaScript code published to npm in the `react-native` package. Currently, React Native ships JavaScript files that contain Flow annotations plus separate TypeScript definition files (`.d.ts`). This proposal would strip the Flow annotations from the JavaScript source files while maintaining the existing TypeScript definitions and adding Flow definition files (`.js.flow`) for Flow users. + +## Basic example + +**Current state** (what gets published to npm): + +React Native currently publishes JavaScript files containing Flow annotations plus separate TypeScript definition files. Here's a real example from React Native's [ActivityIndicator component](https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Components/ActivityIndicator/ActivityIndicator.js): + +```javascript +// Current: Published with Flow annotations +import type {HostComponent} from '../../../src/private/types/HostComponent'; +import type {ViewProps} from '../View/ViewPropTypes'; +import StyleSheet, {type ColorValue} from '../../StyleSheet/StyleSheet'; + +type IndicatorSize = number | 'small' | 'large'; + +type ActivityIndicatorIOSProps = $ReadOnly<{ + hidesWhenStopped?: ?boolean, +}>; + +export type ActivityIndicatorProps = $ReadOnly<{ + ...ViewProps, + ...ActivityIndicatorIOSProps, + animating?: ?boolean, + color?: ?ColorValue, + size?: ?IndicatorSize, +}>; + +const ActivityIndicator: component( + ref?: React.RefSetter>, + ...props: ActivityIndicatorProps +) = ({ + ref: forwardedRef, + animating = true, + color = Platform.OS === 'ios' ? GRAY : null, + // ... other props +}) => { + // Component implementation +}; +``` + +**Proposed state** (what would get published to npm): + +```javascript +// react-native/Libraries/Components/ActivityIndicator/ActivityIndicator.js +// Plain JavaScript (Flow annotations stripped) +import StyleSheet from '../../StyleSheet/StyleSheet'; +import Platform from '../../Utilities/Platform'; +import View from '../View/View'; + +const ActivityIndicator = ({ + ref: forwardedRef, + animating = true, + color = Platform.OS === 'ios' ? GRAY : null, + // ... other props +}) => { + // Component implementation +}; +``` + +**Flow definitions:** +```javascript +// react-native/Libraries/Components/ActivityIndicator/ActivityIndicator.js.flow +// @flow + +import type {HostComponent} from '../../../src/private/types/HostComponent'; +import type {ViewProps} from '../View/ViewPropTypes'; +import type {ColorValue} from '../../StyleSheet/StyleSheet'; + +type IndicatorSize = number | 'small' | 'large'; + +type ActivityIndicatorIOSProps = $ReadOnly<{ + hidesWhenStopped?: ?boolean, +}>; + +export type ActivityIndicatorProps = $ReadOnly<{ + ...ViewProps, + ...ActivityIndicatorIOSProps, + animating?: ?boolean, + color?: ?ColorValue, + size?: ?IndicatorSize, +}>; + +/* Use canonical Flow syntax for the component */ +declare export function ActivityIndicator( + props: ActivityIndicatorProps, + ref?: React.Ref> +): React.Node; +``` + +## Motivation + +### Why are we doing this? + +React Native currently ships JavaScript files that contain Flow annotations plus separate TypeScript definition files (`.d.ts`) to npm. While maintaining dual type support was pragmatic when the ecosystem was split between Flow and TypeScript, the Flow annotations embedded in the JavaScript source code have become a **significant barrier** to adopting modern JavaScript tooling, even though separate TypeScript definitions already exist. + +### What problems does this solve? + +#### 1. **Compatibility with Modern JavaScript Tooling** + +Modern build tools and test runners increasingly do not support Flow syntax out of the box: +- **SWC and @swc/jest**: Fast Rust-based transpiler that is becoming the standard for performance-conscious projects. There is no official Flow support. +- **esbuild**: Ultra-fast go-based bundler. +- **Vitest**: Modern test runner that's gaining popularity as a Jest alternative. + +Projects using these tools must add extra transformation layers specifically to handle Flow syntax from `react-native`, adding complexity and slowing build times. + +#### 2. **Build Efficiency** + +Currently, `react-native-babel-preset` strips Flow types on every build using `@babel/plugin-transform-flow-strip-types`. This means every developer's build process repeatedly performs the same Flow stripping operation. Stripping Flow types once at publish time would be more efficient and eliminate this redundant work from every build. Additionally, shipping plain JavaScript would open the door for React Native to potentially use bundlers other than Babel in the future. + +#### 3. **Alignment with Ecosystem Standards** + +- **Most major libraries ship plain JavaScript**: React and other major frameworks publish plain JavaScript with separate type definitions rather than type-annotated source code. + +### What is the expected outcome? + +After this change: +1. React Native will be **compatible with all modern JavaScript tooling** without additional configuration +2. React Native will be **future-proof** as the ecosystem continues evolving toward standard JavaScript +3. Build times and test times can be **significantly improved** through modern tooling adoption + + +## Detailed design (TBD) + +Add a Flow stripping step to the React Native release process: + +1. **Build Process**: Strip Flow annotations using `@babel/plugin-transform-flow-strip-types` before publishing +2. **Package Structure**: Publish plain JavaScript + existing TypeScript definitions + `.js.flow` files +3. **Source Repository**: Keep Flow annotations in source + +## Drawbacks + +- **Build Pipeline**: Adds a Flow stripping step to the release process +- **Flow Users**: May perceive this as reduced Flow support (though `.js.flow` files maintain compatibility) + +## Alternatives + +The only alternative is to keep Flow annotations in JavaScript files and require each tool (SWC, esbuild, etc.) to strip Flow types using `flow-remove-types` or equivalent (Rust-based for SWC, Go-based for esbuild etc). However, this would be a repetitive task executed each time these tools run, causing performance loss compared to shipping plain JavaScript. + +## Adoption strategy + +Since this is a non-breaking change (`.js.flow` files maintain Flow compatibility), it can be shipped in any release with no migration needed. + +## How we teach this + +This change can be documented in the release notes. + +## Unresolved questions + +- Should we publish `.js.flow` files for Flow users, or drop Flow support entirely? + From 3725814803cb5db18ddc8d629fd2f67aadf8d859 Mon Sep 17 00:00:00 2001 From: bacarybruno Date: Sun, 19 Oct 2025 23:29:57 +0200 Subject: [PATCH 2/4] docs: remove example of Flow annotations from published package proposal --- proposals/0000-strip-flow-types-from-published-package.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/proposals/0000-strip-flow-types-from-published-package.md b/proposals/0000-strip-flow-types-from-published-package.md index c29fc39..d50a992 100644 --- a/proposals/0000-strip-flow-types-from-published-package.md +++ b/proposals/0000-strip-flow-types-from-published-package.md @@ -15,8 +15,6 @@ This proposal seeks to remove Flow type annotations from the JavaScript code pub **Current state** (what gets published to npm): -React Native currently publishes JavaScript files containing Flow annotations plus separate TypeScript definition files. Here's a real example from React Native's [ActivityIndicator component](https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Components/ActivityIndicator/ActivityIndicator.js): - ```javascript // Current: Published with Flow annotations import type {HostComponent} from '../../../src/private/types/HostComponent'; From 4f3eb57bbc21619c44df0c676c291b48a89e71d3 Mon Sep 17 00:00:00 2001 From: bacarybruno Date: Mon, 20 Oct 2025 08:41:12 +0200 Subject: [PATCH 3/4] docs: improve wording --- ...strip-flow-types-from-published-package.md | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/proposals/0000-strip-flow-types-from-published-package.md b/proposals/0000-strip-flow-types-from-published-package.md index d50a992..9481912 100644 --- a/proposals/0000-strip-flow-types-from-published-package.md +++ b/proposals/0000-strip-flow-types-from-published-package.md @@ -2,7 +2,7 @@ title: Strip Flow Types from React Native Published Package author: - Bruno Bodian -date: 2025-10-19 +date: 2025-10-20 --- # RFC0000: Strip Flow Types from React Native Published Package @@ -101,15 +101,15 @@ declare export function ActivityIndicator( ### Why are we doing this? -React Native currently ships JavaScript files that contain Flow annotations plus separate TypeScript definition files (`.d.ts`) to npm. While maintaining dual type support was pragmatic when the ecosystem was split between Flow and TypeScript, the Flow annotations embedded in the JavaScript source code have become a **significant barrier** to adopting modern JavaScript tooling, even though separate TypeScript definitions already exist. +The JavaScript tooling ecosystem is moving fast. There are very good alternatives, that are mainly rust and go-based, which are created to improve the build speed and performance. As of today, React Native still ships Flow code on npm, relying on userland babel to strip these tags even though most React Native developers don't use Flow. ### What problems does this solve? #### 1. **Compatibility with Modern JavaScript Tooling** Modern build tools and test runners increasingly do not support Flow syntax out of the box: -- **SWC and @swc/jest**: Fast Rust-based transpiler that is becoming the standard for performance-conscious projects. There is no official Flow support. -- **esbuild**: Ultra-fast go-based bundler. +- **SWC and @swc/jest**: Fast Rust-based transpiler that is becoming the standard for performance-conscious projects. Flow syntax is not officially supported. They don't plan to support Flow https://github.com/swc-project/swc/issues/256. +- **esbuild**: Fast go-based bundler, doesn't plan to support Flow either. See https://github.com/evanw/esbuild/issues/79 - **Vitest**: Modern test runner that's gaining popularity as a Jest alternative. Projects using these tools must add extra transformation layers specifically to handle Flow syntax from `react-native`, adding complexity and slowing build times. @@ -129,14 +129,22 @@ After this change: 2. React Native will be **future-proof** as the ecosystem continues evolving toward standard JavaScript 3. Build times and test times can be **significantly improved** through modern tooling adoption +## Detailed design -## Detailed design (TBD) +React Native's publishing pipeline would gain a Flow stripping step: -Add a Flow stripping step to the React Native release process: +1. **Build Process**: + - Use `@babel/plugin-transform-flow-strip-types` or `flow-remove-types` to remove Flow annotations at publish time. + - Preserve the original Flow-typed sources as `.js.flow` files. -1. **Build Process**: Strip Flow annotations using `@babel/plugin-transform-flow-strip-types` before publishing -2. **Package Structure**: Publish plain JavaScript + existing TypeScript definitions + `.js.flow` files -3. **Source Repository**: Keep Flow annotations in source +2. **Package Structure:** + The npm package would include: + - Plain `.js` files → Flow annotations stripped + - Existing `.d.ts` files for TypeScript + - Matching `.js.flow` files for Flow users + +3. **Source Repository:** + The React Native source would remain Flow-typed internally. Only the published output changes. ## Drawbacks @@ -145,11 +153,11 @@ Add a Flow stripping step to the React Native release process: ## Alternatives -The only alternative is to keep Flow annotations in JavaScript files and require each tool (SWC, esbuild, etc.) to strip Flow types using `flow-remove-types` or equivalent (Rust-based for SWC, Go-based for esbuild etc). However, this would be a repetitive task executed each time these tools run, causing performance loss compared to shipping plain JavaScript. +The alternative is to keep Flow annotations in JavaScript files and require each tool (SWC, esbuild, etc.) to strip Flow types using `flow-remove-types` or equivalent (Rust-based for SWC, Go-based for esbuild etc). However, this would be a repetitive task executed each time these tools run, causing performance loss compared to shipping plain JavaScript. ## Adoption strategy -Since this is a non-breaking change (`.js.flow` files maintain Flow compatibility), it can be shipped in any release with no migration needed. +Since this is a non-breaking change (`.js.flow` files maintain Flow compatibility), this can be shipped in any release with no migration needed. ## How we teach this @@ -158,4 +166,4 @@ This change can be documented in the release notes. ## Unresolved questions - Should we publish `.js.flow` files for Flow users, or drop Flow support entirely? - +- If `.js.flow` files are included, should we strip implementation bodies and only keep the type declarations to reduce npm package size? From 2d38df5adb7f0b3db8ddb0fa92e3e2ddf333a51d Mon Sep 17 00:00:00 2001 From: bacarybruno Date: Mon, 20 Oct 2025 08:42:30 +0200 Subject: [PATCH 4/4] chore: remove useless comment --- proposals/0000-strip-flow-types-from-published-package.md | 1 - 1 file changed, 1 deletion(-) diff --git a/proposals/0000-strip-flow-types-from-published-package.md b/proposals/0000-strip-flow-types-from-published-package.md index 9481912..08c9b9b 100644 --- a/proposals/0000-strip-flow-types-from-published-package.md +++ b/proposals/0000-strip-flow-types-from-published-package.md @@ -90,7 +90,6 @@ export type ActivityIndicatorProps = $ReadOnly<{ size?: ?IndicatorSize, }>; -/* Use canonical Flow syntax for the component */ declare export function ActivityIndicator( props: ActivityIndicatorProps, ref?: React.Ref>