Compare commits

..
74 Commits
Author SHA1 Message Date
Distiller 91a3a548e2 [0.68.6] Bump version numbers 2023-01-30 18:00:30 +00:00
Nick Gerleman 2990a1b4b8 Mitigation for Samsung TextInput Hangs (#35967)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/35967

In https://github.com/facebook/react-native/issues/35936 we observed that the presence of AbsoluteSizeSpan may lead to hangs when using the Grammarly keyboard on Samsung.

This mitigation makes it so that we do not emit this span in any case where it is sufficient to rely on already set EditText textSize. In simple cases, tested on two devices, it causes typing into the TextInput to no longer hang.

This does not fully resolve the issue for TextInputs which meaningfully use layout-effecting spans (or at least font size), such as non-uniform text size within the input. We instead just try to reduce to minimum AbsoluteSizeSpan possible.

Testing the first commit was able to resolve hangs in some simpler inputs tested, by me and cortinico.

Changelog:
[Android][Fixed] - Mitigation for Samsung TextInput Hangs

Reviewed By: cortinico

Differential Revision: D42721684

fbshipit-source-id: e0388dfb4617f0217bc1d0b71752c733e10261dd
2023-01-30 17:13:59 +00:00
Nicola Corti a0ae706fab Remove PAT_TOKEN and PAT_USERNAME from CircleCI (#35808)
Summary:
This is a cleanup change needed after the CircleCI security incident.
We should be using the `Authorization: Bearer` header to trigger `rn-diff-purge` instead of using username & password authentication.

Source: https://docs.github.com/rest/reference/repos#create-a-repository-dispatch-event

## Changelog

[INTERNAL] - Remove PAT_TOKEN and PAT_USERNAME from CircleCI

Pull Request resolved: https://github.com/facebook/react-native/pull/35808

Test Plan:
I've tested this locally with:

```
curl -X POST https://api.github.com/repos/react-native-community/rn-diff-purge/dispatches \
                    -H "Accept: application/vnd.github.v3+json" \
                    -H "Authorization: Bearer [...]" \
                    -d "{\"event_type\": \"publish\", \"client_payload\": { \"version\": \"test.test.test\" }}"
```

and the run was succesfully fired by react-native-bot:
https://github.com/react-native-community/rn-diff-purge/actions/runs/3894079133

Reviewed By: lunaleaps

Differential Revision: D42456065

Pulled By: cortinico

fbshipit-source-id: 475e9ca80760522cc08bad37d85c5af6727922d3
2023-01-13 15:57:39 +00:00
Nicola Corti 0b6e5b4f91 Update fingerprint for SSH deploy key (#35791)
Summary:
Rotating a private SSH key we had left to rotate.

## Changelog

[INTERNAL] - Update fingerprint for SSH deploy key

Pull Request resolved: https://github.com/facebook/react-native/pull/35791

Test Plan: n/a

Reviewed By: cipolleschi

Differential Revision: D42413025

Pulled By: cortinico

fbshipit-source-id: 571f304e8cc2dad19c41e5413ed3c7d4c078ba60

# Conflicts:
#	.circleci/config.yml
2023-01-09 14:20:05 +00:00
Distiller b12a7f9e68 [0.68.5] Bump version numbers 2022-11-06 20:13:48 +00:00
Thibault Malbranche edcb3ca996 Force dependencies resolution to minor series for 0.68 2022-11-06 20:46:09 +01:00
Distiller b55518c4bf [0.68.4] Bump version numbers 2022-10-10 13:19:53 +00:00
Dmitry Rykun 40a3ae3613 Bump react-native-codegen to 0.0.18 2022-10-10 10:48:35 +01:00
David 782e0bff4a Add support Promise.allSettled out of box (#34544)
Summary:
Issue: https://github.com/facebook/react-native/issues/30236

[General] [Added] - Added support `Promise.allSettled`

Pull Request resolved: https://github.com/facebook/react-native/pull/34544

Test Plan:
Release notes [`promise@8.2.0`](https://github.com/then/promise/releases/tag/8.2.0)

```tsx
typeof Promise.allSettled // function
```

Reviewed By: motiz88

Differential Revision: D39170057

Pulled By: jacdebug

fbshipit-source-id: 72467285e1ad82510ca8fe3d5f347ec8ae286823
2022-10-10 11:21:30 +02:00
Evan Charlton e22ed7d676 fix: Correctly resolve classes with FindClass(..) (#34533)
Summary:
`JNIEnv`'s `FindClass(..)` function takes the classes in the standard
`foo/bar/Baz` class specification (unless they're special, like arrays).
Specifying them with `Lfoo/bar/Baz;` results in a
`ClassNotFoundException` being raised -- which is especially unhelpful
when intending to re-throw an exception.

The docs for `JNIEnv#FindClass(..)` can be found [here][jnienv].

[jnienv]:
  https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#:~:text=The%20name%20argument,java/lang/String%22

## Changelog

[Android] [Fixed] - Correctly resolve classes with FindClass(..)

Pull Request resolved: https://github.com/facebook/react-native/pull/34533

Test Plan:
No exact test plan. However, if an exception is thrown during the
rendering process, a fatal `ClassNotFoundException` is raised, rather
than having the error be passed up to the `ReactNativeManager`.

Fixes: facebook/yoga#1120

Reviewed By: amir-shalem

Differential Revision: D39133326

Pulled By: jacdebug

fbshipit-source-id: 86283b7d21aed49ed0e9027b2aef85f0108cdf9a
2022-10-10 11:18:52 +02:00
Tommy Nguyen 4575ef516f Fix codegen trying to parse .d.ts files (#34439)
Summary:
With react-native 0.70-rc.3 and new arch, codegen may fail if it encounters `.d.ts` files because specs may appear to be unused.

## Changelog

[General] [Fixed] - Codegen should ignore `.d.ts` files

Pull Request resolved: https://github.com/facebook/react-native/pull/34439

Test Plan:
See repro in https://github.com/microsoft/react-native-test-app/pull/1052. The build will fail without manually patching this in.

If you prefer to use your own test app, try adding [react-native-safe-area-context](https://github.com/th3rdwave/react-native-safe-area-context) as a dependency.

Reviewed By: cipolleschi

Differential Revision: D38826388

Pulled By: cortinico

fbshipit-source-id: eb7c9be2d49286bae86b2428862fbf20f6f32ca5
2022-10-10 11:18:36 +02:00
lbaldy 4f7eb6197f prevent from publishing dimensions change event when app changes state (#34014)
Summary:
This fix solves a problem very well evaluated [here](https://github.com/Expensify/App/issues/2727) as well as this [one](https://github.com/facebook/react-native/issues/29290).

The issue is that when the app goes to background, in landscape mode, the RCTDeviceInfo code triggers an orientation change event that did not physically happen. Due to that, we get swapped values returned when going back to the app.

I debugged the react-native code, and to me it seems that react native publishes the orientation change event one extra time when switching the state of the app to 'inactive'. Here is what is happening:

1. iPad is in landscape.
2. We move the app to inactive state.
3. Native Code queues portrait orientation change (no such change happened physically), and immediately after it triggers landscape change (same as we had in point 1).
4. We restore the app to active state.
5. The app receives two queued orientation change events, one after another.
6. The quick transition between portrait and landscape happens even though it never went back to portrait.

Fresh `react-native init` app repro case can be found here: https://github.com/lbaldy/issue-34014-repro

Video presenting the issue (recorded while working on: https://github.com/Expensify/App/issues/2727 ): https://www.youtube.com/watch?v=nFDOml9M8w4

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->
[iOS] [Fixed] - Fix the way the orientation events are published, to avoid false publish on orientation change when app changes state to inactive

Pull Request resolved: https://github.com/facebook/react-native/pull/34014

Test Plan:

1. Make sure you have a working version of E/App.
2. Open App/src/components/withWindowDimensions.js and update the constructor by changing this line:

`this.onDimensionChange = _.debounce(this.onDimensionChange.bind(this), 100);`

to

`this.onDimensionChange = this.onDimensionChange.bind(this);`

3. Open the NewExpensify.xcodeproj in xCode.
4. Open the RCTDeviceInfo.mm file and replace it's contents with the file from this PR.
5. Select your device of choice (I suggest starting with iPad mini) and run the app though xCode.
6. From this point you can move to the test scenarios described below.

Reproduction + Fix test video (Test 1): https://youtu.be/jyzoNHLYHPo
Reproduction + Fix test video (Test 2): https://youtu.be/CLimE-Fba-g

**Test 1:**
1. Launch app in portrait, open chat - no sidebar visible.
7. Switch to landscape - sidebar shows.
8. Put app to background.
9. Put app back to foreground - make sure the side menu doesn't flicker.

**Test 2:**
1. Launch app in portrait, open chat - no sidebar visible.
2. Switch to landscape - sidebar shows.
3. Put app to background. Switch orientation back to portrait.
4. Put app back to foreground - make sure the side menu hides again as it should be in portrait.

Reproduction + Fix test video (Test 3, Test 4): https://youtu.be/EJkUUQCiLRg

iPad mini test 1 applies.

Scenario 2 does not as the screen is too wide in both orientations and iPad pro shows sidebar always.

**Test 3:**

1.  launch the app.
2. Make sure you're in landscape mode.
3. See split screen with some other app. Make sure the side bar is visible.
4. Play with the size of the view, resize it a bit. When the view shrinks it should hide the sidebar, when it grows it should show it.
10. Move the app to background and back to foreground, please observe there are no flickers.

**Test 4:**

1.  Launch the app.
2. Make sure you're in landscape mode.
3. Make the multitasking view and make Expensify app a slide over app.
4. Move back to fullscreen/split screen. Make sure the menu is shown accordingly
5. Move the app to background and back to foreground, please observe there are no flickers.

Non reg with and without the fix video: https://youtu.be/kuv9in8vtbk

Please perform standard smoke tests on transformation changes.

Reviewed By: cipolleschi

Differential Revision: D37239891

Pulled By: jacdebug

fbshipit-source-id: e6090153820e921dcfb0d823e0377abd25225bdf
2022-10-10 11:17:51 +02:00
Distiller 36153e2557 [0.68.3] Bump version numbers 2022-08-08 10:23:48 +00:00
Lorenzo Sciandra d2d244cb8f [LOCAL] align pods version config with the rest of the repo 2022-08-05 14:25:41 +01:00
Sparsha SahaandSparsha Saha af9225ec5f Let's not build reactnativeutilsjni shared library (#34345)
Co-authored-by: Sparsha Saha
2022-08-05 14:22:50 +01:00
Kunal Farmah 4555fc7865 Added additional builder method receiving arguments for using jsc or hermes to correctly decide which DSO to load at app startup. (#33952)
Summary:
The current implementation of **getDefaultJSExecutorFactory** relies solely on try catch to load the correct .so file for jsc or hermes based on the project configuration.
Relying solely on try catch block and loading jsc even when project is using hermes can lead to launch time crashes especially in monorepo architectures and hybrid apps using both native android and react native.
So we can make use of an additional **ReactInstanceManager :: setJsEngineAsHermes** method that accepts a Boolean argument from the host app while building ReactInstanceManager which can tell which library to load at startup in **ReactInstanceManagerBuilder** which will now have an enhanced getDefaultJSExecutorFactory method that will combine the old logic with the new one to load the dso files.

The code snippet in **ReactInstanceManager** for adding a new setter method:

```
  /**
   * Sets the jsEngine as JSC or HERMES as per the setJsEngineAsHermes call
   * Uses the enum {link JSInterpreter}
   * param jsEngine
   */
  private void setJSEngine(JSInterpreter jsEngine){
    this.jsEngine = jsEngine;
  }

  /**
   * Utility setter to set the required JSEngine as HERMES or JSC
   * Defaults to OLD_LOGIC if not called by the host app
   * param hermesEnabled
   * hermesEnabled = true sets the JS Engine as HERMES and JSC otherwise
   */
  public ReactInstanceManagerBuilder setJsEngineAsHermes(boolean hermesEnabled){
    if(hermesEnabled){
      setJSEngine(JSInterpreter.HERMES);
    }
    else{
      setJSEngine(JSInterpreter.JSC);
    }
    return this;
  }
```

The code snippet for the new logic in **ReactInstanceManagerBuilder**:

1) Setting up the new logic:
Adding a new enum class :
```
  public enum JSInterpreter {
    OLD_LOGIC,
    JSC,
    HERMES
  }
```

A setter getting boolean value telling whether to use hermes or not and calling a private setter to update the enum variable.
```
 /**
   * Sets the jsEngine as JSC or HERMES as per the setJsEngineAsHermes call
   * Uses the enum {link JSInterpreter}
   * param jsEngine
   */
  private void setJSEngine(JSInterpreter jsEngine){
    this.jsEngine = jsEngine;
  }

  /**
   * Utility setter to set the required JSEngine as HERMES or JSC
   * Defaults to OLD_LOGIC if not called by the host app
   * param hermesEnabled
   * hermesEnabled = true sets the JS Engine as HERMES and JSC otherwise
   */
  public ReactInstanceManagerBuilder setJsEngineAsHermes(boolean hermesEnabled){
    if(hermesEnabled){
      setJSEngine(JSInterpreter.HERMES);
    }
    else{
      setJSEngine(JSInterpreter.JSC);
    }
    return this;
  }
```

2) Modifying the getDefaultJSExecutorFactory method to incorporate the new logic with the old one:

```
   private JavaScriptExecutorFactory getDefaultJSExecutorFactory(
    String appName, String deviceName, Context applicationContext) {

    // Relying solely on try catch block and loading jsc even when
    // project is using hermes can lead to launch-time crashes especially in
    // monorepo architectures and hybrid apps using both native android
    // and react native.
    // So we can use the value of enableHermes received by the constructor
    // to decide which library to load at launch

    // if nothing is specified, use old loading method
    // else load the required engine
    if (jsEngine == JSInterpreter.OLD_LOGIC) {
      try {
        // If JSC is included, use it as normal
        initializeSoLoaderIfNecessary(applicationContext);
        JSCExecutor.loadLibrary();
        return new JSCExecutorFactory(appName, deviceName);
      } catch (UnsatisfiedLinkError jscE) {
        if (jscE.getMessage().contains("__cxa_bad_typeid")) {
          throw jscE;
        }
        HermesExecutor.loadLibrary();
        return new HermesExecutorFactory();
      }
    } else if (jsEngine == JSInterpreter.HERMES) {
      HermesExecutor.loadLibrary();
      return new HermesExecutorFactory();
    } else {
      JSCExecutor.loadLibrary();
      return new JSCExecutorFactory(appName, deviceName);
    }
  }
```

### **Suggested changes in any Android App's MainApplication that extends ReactApplication to take advantage of this fix**
```
builder = ReactInstanceManager.builder()
                .setApplication(this)
                .setJsEngineAsHermes(BuildConfig.HERMES_ENABLED)
                .setBundleAssetName("index.android.bundle")
                .setJSMainModulePath("index")
```

where HERMES_ENABLED is a buildConfigField based on the enableHermes flag in build.gradle:

`def enableHermes = project.ext.react.get("enableHermes", true)
`
and then

```
defaultConfig{
if(enableHermes) {
            buildConfigField("boolean", "HERMES_ENABLED", "true")
        }
        else{
            buildConfigField("boolean", "HERMES_ENABLED", "false")
        }
}
```

Our app was facing a similar issue as listed in this list:  **https://github.com/facebook/react-native/issues?q=is%3Aissue+is%3Aopen+DSO**. Which was react-native trying to load jsc even when our project used hermes when a debug build was deployed on a device using android studio play button.

This change can possibly solve many of the issues listed in the list as it solved ours.

## Changelog

[GENERAL] [ADDED] - An enum JSInterpreter  in com.facebook.react package:
```
/**
 * An enum that specifies the JS Engine to be used in the app
 * Old Logic uses the legacy code
 * JSC/HERMES loads the respective engine using the revamped logic
 */
public enum JSInterpreter {
  OLD_LOGIC,
  JSC,
  HERMES
}
```

[GENERAL] [ADDED] - An enum variable storing the default value of Js Engine loading mechanism in ReactInstanceManagerBuilder:

```
   private JSInterpreter  jsEngine = JSInterpreter.OLD_LOGIC;
```

[GENERAL] [ADDED] - A new setter method and a helper method to set the js engine in ReactInstanceManagerBuilder:
```
  /**
   * Sets the jsEngine as JSC or HERMES as per the setJsEngineAsHermes call
   * Uses the enum {link JSInterpreter}
   * param jsEngine
   */
  private void setJSEngine(JSInterpreter jsEngine){
    this.jsEngine = jsEngine;
  }

  /**
   * Utility setter to set the required JSEngine as HERMES or JSC
   * Defaults to OLD_LOGIC if not called by the host app
   * param hermesEnabled
   * hermesEnabled = true sets the JS Engine as HERMES and JSC otherwise
   */
  public ReactInstanceManagerBuilder setJsEngineAsHermes(boolean hermesEnabled){
    if(hermesEnabled){
      setJSEngine(JSInterpreter.HERMES);
    }
    else{
      setJSEngine(JSInterpreter.JSC);
    }
    return this;
  }

```

[GENERAL] [ADDED] - Modified **getDefaultJSExecutorFactory** method

```
private JavaScriptExecutorFactory getDefaultJSExecutorFactory(
    String appName, String deviceName, Context applicationContext) {

    // Relying solely on try catch block and loading jsc even when
    // project is using hermes can lead to launch-time crashes especially in
    // monorepo architectures and hybrid apps using both native android
    // and react native.
    // So we can use the value of enableHermes received by the constructor
    // to decide which library to load at launch

    // if nothing is specified, use old loading method
    // else load the required engine
    if (jsEngine == JSInterpreter.OLD_LOGIC) {
      try {
        // If JSC is included, use it as normal
        initializeSoLoaderIfNecessary(applicationContext);
        JSCExecutor.loadLibrary();
        return new JSCExecutorFactory(appName, deviceName);
      } catch (UnsatisfiedLinkError jscE) {
        if (jscE.getMessage().contains("__cxa_bad_typeid")) {
          throw jscE;
        }
        HermesExecutor.loadLibrary();
        return new HermesExecutorFactory();
      }
    } else if (jsEngine == JSInterpreter.HERMES) {
      HermesExecutor.loadLibrary();
      return new HermesExecutorFactory();
    } else {
      JSCExecutor.loadLibrary();
      return new JSCExecutorFactory(appName, deviceName);
    }
  }
```

Pull Request resolved: https://github.com/facebook/react-native/pull/33952

Test Plan:
The testing for this change might be tricky but can be done by following the reproduction steps in the issues related to DSO loading here: https://github.com/facebook/react-native/issues?q=is%3Aissue+is%3Aopen+DSO

Generally, the app will not crash anymore on deploying debug using android studio if we are removing libjsc and its related libraries in **packagingOptions** in build.gradle and using hermes in the project.
It can be like:
```
packagingOptions {
        if (enableHermes) {
            exclude "**/libjsc*.so"
        }
    }
```

Reviewed By: lunaleaps

Differential Revision: D37191981

Pulled By: cortinico

fbshipit-source-id: c528ead126939f1d788af7523f3798ed2a14f36e
2022-08-02 12:25:43 +01:00
Olivier Payen 989546f97e Use monotonic clock for performance.now() (#33983)
Summary:
In https://github.com/facebook/react-native/pull/32695, the `Performance.now()` implementation changed to use unix epoch timestamps instead of a monotonic clock.

This is problematic, because it means that performance measurements get skewed if the device clock changes between two measurements.

With this change, the clock is now monotonic (and the implementation stays consistent between platforms).

More details and repro steps can be found in [this issue](https://github.com/facebook/react-native/issues/33977)
Closes https://github.com/facebook/react-native/issues/33977

## Changelog

[General] [Fixed] - Use monotonic clock for performance.now()

Pull Request resolved: https://github.com/facebook/react-native/pull/33983

Test Plan:
Run on iOS and Android:
```
const now = global.performance.now()
console.log(`${Platform.OS}: ${now}`)
```

Reviewed By: JoshuaGross, cipolleschi

Differential Revision: D37066999

Pulled By: dmitryrykun

fbshipit-source-id: 298547bf39faea1b025c17ff2d2e1a03f929865b
2022-08-02 12:25:34 +01:00
Kunal Farmah ec3688f95a Logging a soft error when ReactRootView has an id other than -1 instead of crashing the app in hybrid apps. (#33133)
Summary:
As per commit: https://github.com/facebook/react-native/commit/4f3b17412018a10f9293247c802598d2b94a844b which states that "React Native requires that the RootView id be managed entirely by React Native, and will crash in addRootView/startSurface if the native View id isn't set to NO_ID."

This behaviour can not be guaranteed in **hybrid** apps that have a native android layer over which ReactRootViews are added and the native views need to have ids on them in order to work. **The control of views can jump back and forth between native android and react-native (fabric). As the ReactRootView is added to ViewGroups (or layouts) in Android Fragments and Activities, they contain ids on their views which might get passed down to the reactRootView by features like DataBinding**

Hence this can cause unnecessary crashes at runtime for hybrid apps even when they are not changing the id of the reactRootView object they are adding to their ViewGroups.

Our app is a hybrid app that uses both native android and react-native on different screens and on one such screen that has a Fragment adding a ReactRootView to its FrameLayout to render native android views to render in ReactNative, this crash occurs on pressing the back button as well as on unlocking the screen while staying on the same screen.

The app was running fine on more than a 100 million devices on React Native 0.63.4 but after updating to 0.67.2, that features this commit, it crashes on the very first device it was tested on.

Refer to the issue: https://github.com/facebook/react-native/issues/33121 for more information on the crash

The fragment in which this issues arises is like this:

 ```binding.frameLayout.addView(getReactRootView())```

where getReactRootView() is like this:

```
    private var mReactRootView: ReactRootView? = null
    private var mReactInstanceManager: ReactInstanceManager? = null

    mReactRootView = ReactRootView(context)

        if (activity != null) {
            val application = activity?.application
            if (application is MainApplication) {
                mReactInstanceManager = application.reactInstanceManager
            }
        }

      fun getReactRootView():View?{
         return  mReactRootView
      }
```

So converting this to a soft exception such that pure react-native devs can still see the error while hybrid apps continue to run without crashes.

### Snippet of the change:

```
if (getId() != View.NO_ID) {
        ReactSoftExceptionLogger.logSoftException(
            TAG,
            new IllegalViewOperationException(
              "Trying to attach a ReactRootView with an explicit id already set to ["
                  + getId()
                  + "]. React Native uses the id field to track react tags and will overwrite this"
                  + " field. If that is fine, explicitly overwrite the id field to View.NO_ID."));
    }
```

## Changelog

[GENERAL] [ADDED] - A ReactSoftException log instead of a direct exception being thrown:

```
if (getId() != View.NO_ID) {
        ReactSoftExceptionLogger.logSoftException(
            TAG,
            new IllegalViewOperationException(
              "Trying to attach a ReactRootView with an explicit id already set to ["
                  + getId()
                  + "]. React Native uses the id field to track react tags and will overwrite this"
                  + " field. If that is fine, explicitly overwrite the id field to View.NO_ID."));
    }
```

[GENERAL] [REMOVED]- Directly throwing an exception even when the code is not responsible for this issue:

```
if (getId() != View.NO_ID) {
      throw new IllegalViewOperationException(
          "Trying to attach a ReactRootView with an explicit id already set to ["
              + getId()
              + "]. React Native uses the id field to track react tags and will overwrite this"
              + " field. If that is fine, explicitly overwrite the id field to View.NO_ID.");
    }

```

Pull Request resolved: https://github.com/facebook/react-native/pull/33133

Test Plan:
This crash is hard to reproduce but when it occurs, this is the only way to fix it.
If any app used to crash with this exact error, it will no longer crash but show an error log in Logcat for developers to be informed about the issue.

Reviewed By: ShikaSD

Differential Revision: D34304212

Pulled By: cortinico

fbshipit-source-id: f0eaeef2e905a6e0587df088b43cc49cabda397a
2022-08-02 12:25:21 +01:00
Lorenzo Sciandra 25e2cbb441 [LOCAL] change ruby version for source 2022-08-02 12:24:54 +01:00
Nicola Corti 1fad3af37c Update script from prepublish (deprecated) to prepack (#34198)
Summary:
Currently `react-native-codegen` uses `prepublish` to pre-build before publishing. Moving to `prepare` as `prepublish` is deprecated and not invoked anymore:
https://docs.npmjs.com/cli/v8/using-npm/scripts#life-cycle-scripts

## Changelog

[Internal][Fixed] - [codegen] Update script from prepublish (deprecated) to prepack

Pull Request resolved: https://github.com/facebook/react-native/pull/34198

Test Plan:
Tested locally with:

```
cd packages/react-native-codegen/ && rm -rf lib && npm publish --dry-run --foreground-scripts
```

output is:

```

> react-native-codegen@0.70.1 prepare
> yarn run build

yarn run v1.22.18
$ yarn clean && node scripts/build.js --verbose
$ rm -rf lib
react-native-codegen...........................................................  • src/__tests__/__snapshots__/SchemaValidator-test.js.snap (ignore)
  • src/__tests__/SchemaValidator-test.js (ignore)
  • src/cli/combine/combine-js-to-schema-cli.js ⇒ lib/cli/combine/combine-js-to-schema-cli.js
  • src/cli/combine/combine-js-to-schema.js ⇒ lib/cli/combine/combine-js-to-schema.js
  • src/cli/generators/generate-all.js ⇒ lib/cli/generators/generate-all.js
  • src/cli/parser/parser-cli.js ⇒ lib/cli/parser/parser-cli.js
  • src/cli/parser/parser.js ⇒ lib/cli/parser/parser.js
  • src/cli/parser/parser.sh ⇒ lib/cli/parser/parser.sh (copy)
  • src/CodegenSchema.js ⇒ lib/CodegenSchema.js
  • src/generators/__test_fixtures__/fixtures.js ⇒ lib/generators/__test_fixtures__/fixtures.js
  • src/generators/__tests__/RNCodegen-test.js (ignore)
  • src/generators/components/__test_fixtures__/fixtures.js ⇒ lib/generators/components/__test_fixtures__/fixtures.js
  • src/generators/components/__tests__/__snapshots__/GenerateComponentDescriptorH-test.js.snap (ignore)
  • src/generators/components/__tests__/__snapshots__/GenerateComponentHObjCpp-test.js.snap (ignore)
  • src/generators/components/__tests__/__snapshots__/GenerateEventEmitterCpp-test.js.snap (ignore)
  • src/generators/components/__tests__/__snapshots__/GenerateEventEmitterH-test.js.snap (ignore)
  • src/generators/components/__tests__/__snapshots__/GeneratePropsCpp-test.js.snap (ignore)
  • src/generators/components/__tests__/__snapshots__/GeneratePropsH-test.js.snap (ignore)
  • src/generators/components/__tests__/__snapshots__/GeneratePropsJavaDelegate-test.js.snap (ignore)
  • src/generators/components/__tests__/__snapshots__/GeneratePropsJavaInterface-test.js.snap (ignore)
  • src/generators/components/__tests__/__snapshots__/GeneratePropsJavaPojo-test.js.snap (ignore)
  • src/generators/components/__tests__/__snapshots__/GenerateShadowNodeCpp-test.js.snap (ignore)
  • src/generators/components/__tests__/__snapshots__/GenerateShadowNodeH-test.js.snap (ignore)
  • src/generators/components/__tests__/__snapshots__/GenerateTests-test.js.snap (ignore)
  • src/generators/components/__tests__/__snapshots__/GenerateThirdPartyFabricComponentsProviderH-test.js.snap (ignore)
  • src/generators/components/__tests__/__snapshots__/GenerateThirdPartyFabricComponentsProviderObjCpp-test.js.snap (ignore)
  • src/generators/components/__tests__/__snapshots__/GenerateViewConfigJs-test.js.snap (ignore)
  • src/generators/components/__tests__/GenerateComponentDescriptorH-test.js (ignore)
  • src/generators/components/__tests__/GenerateComponentHObjCpp-test.js (ignore)
  • src/generators/components/__tests__/GenerateEventEmitterCpp-test.js (ignore)
  • src/generators/components/__tests__/GenerateEventEmitterH-test.js (ignore)
  • src/generators/components/__tests__/GeneratePropsCpp-test.js (ignore)
  • src/generators/components/__tests__/GeneratePropsH-test.js (ignore)
  • src/generators/components/__tests__/GeneratePropsJavaDelegate-test.js (ignore)
  • src/generators/components/__tests__/GeneratePropsJavaInterface-test.js (ignore)
  • src/generators/components/__tests__/GeneratePropsJavaPojo-test.js (ignore)
  • src/generators/components/__tests__/GenerateShadowNodeCpp-test.js (ignore)
  • src/generators/components/__tests__/GenerateShadowNodeH-test.js (ignore)
  • src/generators/components/__tests__/GenerateTests-test.js (ignore)
  • src/generators/components/__tests__/GenerateThirdPartyFabricComponentsProviderH-test.js (ignore)
  • src/generators/components/__tests__/GenerateThirdPartyFabricComponentsProviderObjCpp-test.js (ignore)
  • src/generators/components/__tests__/GenerateViewConfigJs-test.js (ignore)
  • src/generators/components/CppHelpers.js ⇒ lib/generators/components/CppHelpers.js
  • src/generators/components/GenerateComponentDescriptorH.js ⇒ lib/generators/components/GenerateComponentDescriptorH.js
  • src/generators/components/GenerateComponentHObjCpp.js ⇒ lib/generators/components/GenerateComponentHObjCpp.js
  • src/generators/components/GenerateEventEmitterCpp.js ⇒ lib/generators/components/GenerateEventEmitterCpp.js
  • src/generators/components/GenerateEventEmitterH.js ⇒ lib/generators/components/GenerateEventEmitterH.js
  • src/generators/components/GeneratePropsCpp.js ⇒ lib/generators/components/GeneratePropsCpp.js
  • src/generators/components/GeneratePropsH.js ⇒ lib/generators/components/GeneratePropsH.js
  • src/generators/components/GeneratePropsJavaDelegate.js ⇒ lib/generators/components/GeneratePropsJavaDelegate.js
  • src/generators/components/GeneratePropsJavaInterface.js ⇒ lib/generators/components/GeneratePropsJavaInterface.js
  • src/generators/components/GeneratePropsJavaPojo/index.js ⇒ lib/generators/components/GeneratePropsJavaPojo/index.js
  • src/generators/components/GeneratePropsJavaPojo/PojoCollector.js ⇒ lib/generators/components/GeneratePropsJavaPojo/PojoCollector.js
  • src/generators/components/GeneratePropsJavaPojo/serializePojo.js ⇒ lib/generators/components/GeneratePropsJavaPojo/serializePojo.js
  • src/generators/components/GenerateShadowNodeCpp.js ⇒ lib/generators/components/GenerateShadowNodeCpp.js
  • src/generators/components/GenerateShadowNodeH.js ⇒ lib/generators/components/GenerateShadowNodeH.js
  • src/generators/components/GenerateTests.js ⇒ lib/generators/components/GenerateTests.js
  • src/generators/components/GenerateThirdPartyFabricComponentsProviderH.js ⇒ lib/generators/components/GenerateThirdPartyFabricComponentsProviderH.js
  • src/generators/components/GenerateThirdPartyFabricComponentsProviderObjCpp.js ⇒ lib/generators/components/GenerateThirdPartyFabricComponentsProviderObjCpp.js
  • src/generators/components/GenerateViewConfigJs.js ⇒ lib/generators/components/GenerateViewConfigJs.js
  • src/generators/components/JavaHelpers.js ⇒ lib/generators/components/JavaHelpers.js
  • src/generators/modules/__test_fixtures__/fixtures.js ⇒ lib/generators/modules/__test_fixtures__/fixtures.js
  • src/generators/modules/__tests__/__snapshots__/GenerateModuleCpp-test.js.snap (ignore)
  • src/generators/modules/__tests__/__snapshots__/GenerateModuleH-test.js.snap (ignore)
  • src/generators/modules/__tests__/__snapshots__/GenerateModuleHObjCpp-test.js.snap (ignore)
  • src/generators/modules/__tests__/__snapshots__/GenerateModuleJavaSpec-test.js.snap (ignore)
  • src/generators/modules/__tests__/__snapshots__/GenerateModuleJniCpp-test.js.snap (ignore)
  • src/generators/modules/__tests__/__snapshots__/GenerateModuleJniH-test.js.snap (ignore)
  • src/generators/modules/__tests__/__snapshots__/GenerateModuleMm-test.js.snap (ignore)
  • src/generators/modules/__tests__/GenerateModuleCpp-test.js (ignore)
  • src/generators/modules/__tests__/GenerateModuleH-test.js (ignore)
  • src/generators/modules/__tests__/GenerateModuleHObjCpp-test.js (ignore)
  • src/generators/modules/__tests__/GenerateModuleJavaSpec-test.js (ignore)
  • src/generators/modules/__tests__/GenerateModuleJniCpp-test.js (ignore)
  • src/generators/modules/__tests__/GenerateModuleJniH-test.js (ignore)
  • src/generators/modules/__tests__/GenerateModuleMm-test.js (ignore)
  • src/generators/modules/GenerateModuleCpp.js ⇒ lib/generators/modules/GenerateModuleCpp.js
  • src/generators/modules/GenerateModuleH.js ⇒ lib/generators/modules/GenerateModuleH.js
  • src/generators/modules/GenerateModuleJavaSpec.js ⇒ lib/generators/modules/GenerateModuleJavaSpec.js
  • src/generators/modules/GenerateModuleJniCpp.js ⇒ lib/generators/modules/GenerateModuleJniCpp.js
  • src/generators/modules/GenerateModuleJniH.js ⇒ lib/generators/modules/GenerateModuleJniH.js
  • src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js ⇒ lib/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js
  • src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js ⇒ lib/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js
  • src/generators/modules/GenerateModuleObjCpp/header/serializeStruct.js ⇒ lib/generators/modules/GenerateModuleObjCpp/header/serializeStruct.js
  • src/generators/modules/GenerateModuleObjCpp/index.js ⇒ lib/generators/modules/GenerateModuleObjCpp/index.js
  • src/generators/modules/GenerateModuleObjCpp/serializeMethod.js ⇒ lib/generators/modules/GenerateModuleObjCpp/serializeMethod.js
  • src/generators/modules/GenerateModuleObjCpp/source/serializeModule.js ⇒ lib/generators/modules/GenerateModuleObjCpp/source/serializeModule.js
  • src/generators/modules/GenerateModuleObjCpp/StructCollector.js ⇒ lib/generators/modules/GenerateModuleObjCpp/StructCollector.js
  • src/generators/modules/GenerateModuleObjCpp/Utils.js ⇒ lib/generators/modules/GenerateModuleObjCpp/Utils.js
  • src/generators/modules/Utils.js ⇒ lib/generators/modules/Utils.js
  • src/generators/RNCodegen.js ⇒ lib/generators/RNCodegen.js
  • src/generators/Utils.js ⇒ lib/generators/Utils.js
  • src/parsers/flow/components/__test_fixtures__/failures.js ⇒ lib/parsers/flow/components/__test_fixtures__/failures.js
  • src/parsers/flow/components/__test_fixtures__/fixtures.js ⇒ lib/parsers/flow/components/__test_fixtures__/fixtures.js
  • src/parsers/flow/components/__tests__/__snapshots__/component-parser-test.js.snap (ignore)
  • src/parsers/flow/components/__tests__/component-parser-test.js (ignore)
  • src/parsers/flow/components/commands.js ⇒ lib/parsers/flow/components/commands.js
  • src/parsers/flow/components/events.js ⇒ lib/parsers/flow/components/events.js
  • src/parsers/flow/components/extends.js ⇒ lib/parsers/flow/components/extends.js
  • src/parsers/flow/components/index.js ⇒ lib/parsers/flow/components/index.js
  • src/parsers/flow/components/options.js ⇒ lib/parsers/flow/components/options.js
  • src/parsers/flow/components/props.js ⇒ lib/parsers/flow/components/props.js
  • src/parsers/flow/components/schema.js ⇒ lib/parsers/flow/components/schema.js
  • src/parsers/flow/errors.js ⇒ lib/parsers/flow/errors.js
  • src/parsers/flow/index.js ⇒ lib/parsers/flow/index.js
  • src/parsers/flow/modules/__test_fixtures__/failures.js ⇒ lib/parsers/flow/modules/__test_fixtures__/failures.js
  • src/parsers/flow/modules/__test_fixtures__/fixtures.js ⇒ lib/parsers/flow/modules/__test_fixtures__/fixtures.js
  • src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap (ignore)
  • src/parsers/flow/modules/__tests__/module-parser-e2e-test.js (ignore)
  • src/parsers/flow/modules/__tests__/module-parser-snapshot-test.js (ignore)
  • src/parsers/flow/modules/errors.js ⇒ lib/parsers/flow/modules/errors.js
  • src/parsers/flow/modules/index.js ⇒ lib/parsers/flow/modules/index.js
  • src/parsers/flow/modules/schema.js ⇒ lib/parsers/flow/modules/schema.js
  • src/parsers/flow/modules/utils.js ⇒ lib/parsers/flow/modules/utils.js
  • src/parsers/flow/utils.js ⇒ lib/parsers/flow/utils.js
  • src/parsers/schema/index.js ⇒ lib/parsers/schema/index.js
  • src/parsers/typescript/components/__test_fixtures__/failures.js ⇒ lib/parsers/typescript/components/__test_fixtures__/failures.js
  • src/parsers/typescript/components/__test_fixtures__/fixtures.js ⇒ lib/parsers/typescript/components/__test_fixtures__/fixtures.js
  • src/parsers/typescript/components/__tests__/__snapshots__/typescript-component-parser-test.js.snap (ignore)
  • src/parsers/typescript/components/__tests__/typescript-component-parser-test.js (ignore)
  • src/parsers/typescript/components/commands.js ⇒ lib/parsers/typescript/components/commands.js
  • src/parsers/typescript/components/events.js ⇒ lib/parsers/typescript/components/events.js
  • src/parsers/typescript/components/extends.js ⇒ lib/parsers/typescript/components/extends.js
  • src/parsers/typescript/components/index.js ⇒ lib/parsers/typescript/components/index.js
  • src/parsers/typescript/components/options.js ⇒ lib/parsers/typescript/components/options.js
  • src/parsers/typescript/components/props.js ⇒ lib/parsers/typescript/components/props.js
  • src/parsers/typescript/components/schema.js ⇒ lib/parsers/typescript/components/schema.js
  • src/parsers/typescript/errors.js ⇒ lib/parsers/typescript/errors.js
  • src/parsers/typescript/index.js ⇒ lib/parsers/typescript/index.js
  • src/parsers/typescript/modules/__test_fixtures__/failures.js ⇒ lib/parsers/typescript/modules/__test_fixtures__/failures.js
  • src/parsers/typescript/modules/__test_fixtures__/fixtures.js ⇒ lib/parsers/typescript/modules/__test_fixtures__/fixtures.js
  • src/parsers/typescript/modules/__tests__/__snapshots__/typescript-module-parser-snapshot-test.js.snap (ignore)
  • src/parsers/typescript/modules/__tests__/typescript-module-parser-e2e-test.js (ignore)
  • src/parsers/typescript/modules/__tests__/typescript-module-parser-snapshot-test.js (ignore)
  • src/parsers/typescript/modules/errors.js ⇒ lib/parsers/typescript/modules/errors.js
  • src/parsers/typescript/modules/index.js ⇒ lib/parsers/typescript/modules/index.js
  • src/parsers/typescript/modules/schema.js ⇒ lib/parsers/typescript/modules/schema.js
  • src/parsers/typescript/modules/utils.js ⇒ lib/parsers/typescript/modules/utils.js
  • src/parsers/typescript/utils.js ⇒ lib/parsers/typescript/utils.js
  • src/SchemaValidator.js ⇒ lib/SchemaValidator.js
[  OK  ]
  Done in 2.27s.
npm notice
npm notice 📦  react-native-codegen@0.70.1
npm notice === Tarball Contents ===
npm notice 383B   README.md
npm notice 3.2kB  lib/cli/combine/combine-js-to-schema-cli.js
npm notice 1.8kB  lib/cli/combine/combine-js-to-schema-cli.js.flow
npm notice 2.5kB  lib/cli/combine/combine-js-to-schema.js
npm notice 1.3kB  lib/cli/combine/combine-js-to-schema.js.flow
npm notice 1.5kB  lib/cli/generators/generate-all.js
npm notice 1.4kB  lib/cli/generators/generate-all.js.flow
npm notice 1.7kB  lib/cli/parser/parser-cli.js
npm notice 386B   lib/cli/parser/parser-cli.js.flow
npm notice 777B   lib/cli/parser/parser.js
npm notice 811B   lib/cli/parser/parser.js.flow
npm notice 483B   lib/cli/parser/parser.sh
npm notice 222B   lib/CodegenSchema.js
npm notice 8.8kB  lib/CodegenSchema.js.flow
npm notice 1.8kB  lib/generators/__test_fixtures__/fixtures.js
npm notice 1.9kB  lib/generators/__test_fixtures__/fixtures.js.flow
npm notice 43.6kB lib/generators/components/__test_fixtures__/fixtures.js
npm notice 44.0kB lib/generators/components/__test_fixtures__/fixtures.js.flow
npm notice 5.7kB  lib/generators/components/CppHelpers.js
npm notice 6.4kB  lib/generators/components/CppHelpers.js.flow
npm notice 2.0kB  lib/generators/components/GenerateComponentDescriptorH.js
npm notice 2.3kB  lib/generators/components/GenerateComponentDescriptorH.js.flow
npm notice 9.3kB  lib/generators/components/GenerateComponentHObjCpp.js
npm notice 10.3kB lib/generators/components/GenerateComponentHObjCpp.js.flow
npm notice 6.3kB  lib/generators/components/GenerateEventEmitterCpp.js
npm notice 7.2kB  lib/generators/components/GenerateEventEmitterCpp.js.flow
npm notice 6.5kB  lib/generators/components/GenerateEventEmitterH.js
npm notice 7.4kB  lib/generators/components/GenerateEventEmitterH.js.flow
npm notice 4.0kB  lib/generators/components/GeneratePropsCpp.js
npm notice 4.3kB  lib/generators/components/GeneratePropsCpp.js.flow
npm notice 23.4kB lib/generators/components/GeneratePropsH.js
npm notice 26.1kB lib/generators/components/GeneratePropsH.js.flow
npm notice 9.4kB  lib/generators/components/GeneratePropsJavaDelegate.js
npm notice 10.0kB lib/generators/components/GeneratePropsJavaDelegate.js.flow
npm notice 7.1kB  lib/generators/components/GeneratePropsJavaInterface.js
npm notice 7.8kB  lib/generators/components/GeneratePropsJavaInterface.js.flow
npm notice 2.0kB  lib/generators/components/GeneratePropsJavaPojo/index.js
npm notice 2.1kB  lib/generators/components/GeneratePropsJavaPojo/index.js.flow
npm notice 4.0kB  lib/generators/components/GeneratePropsJavaPojo/PojoCollector.js
npm notice 4.7kB  lib/generators/components/GeneratePropsJavaPojo/PojoCollector.js.flow
npm notice 7.2kB  lib/generators/components/GeneratePropsJavaPojo/serializePojo.js
npm notice 7.5kB  lib/generators/components/GeneratePropsJavaPojo/serializePojo.js.flow
npm notice 2.0kB  lib/generators/components/GenerateShadowNodeCpp.js
npm notice 2.2kB  lib/generators/components/GenerateShadowNodeCpp.js.flow
npm notice 2.8kB  lib/generators/components/GenerateShadowNodeH.js
npm notice 3.1kB  lib/generators/components/GenerateShadowNodeH.js.flow
npm notice 5.3kB  lib/generators/components/GenerateTests.js
npm notice 5.9kB  lib/generators/components/GenerateTests.js.flow
npm notice 2.5kB  lib/generators/components/GenerateThirdPartyFabricComponentsProviderH.js
npm notice 2.7kB  lib/generators/components/GenerateThirdPartyFabricComponentsProviderH.js.flow
npm notice 2.6kB  lib/generators/components/GenerateThirdPartyFabricComponentsProviderObjCpp.js
npm notice 2.8kB  lib/generators/components/GenerateThirdPartyFabricComponentsProviderObjCpp.js.flow
npm notice 13.4kB lib/generators/components/GenerateViewConfigJs.js
npm notice 14.0kB lib/generators/components/GenerateViewConfigJs.js.flow
npm notice 2.7kB  lib/generators/components/JavaHelpers.js
npm notice 3.2kB  lib/generators/components/JavaHelpers.js.flow
npm notice 47.0kB lib/generators/modules/__test_fixtures__/fixtures.js
npm notice 47.2kB lib/generators/modules/__test_fixtures__/fixtures.js.flow
npm notice 8.4kB  lib/generators/modules/GenerateModuleCpp.js
npm notice 7.3kB  lib/generators/modules/GenerateModuleCpp.js.flow
npm notice 8.5kB  lib/generators/modules/GenerateModuleH.js
npm notice 7.0kB  lib/generators/modules/GenerateModuleH.js.flow
npm notice 16.8kB lib/generators/modules/GenerateModuleJavaSpec.js
npm notice 15.6kB lib/generators/modules/GenerateModuleJavaSpec.js.flow
npm notice 14.9kB lib/generators/modules/GenerateModuleJniCpp.js
npm notice 13.9kB lib/generators/modules/GenerateModuleJniCpp.js.flow
npm notice 4.7kB  lib/generators/modules/GenerateModuleJniH.js
npm notice 4.9kB  lib/generators/modules/GenerateModuleJniH.js.flow
npm notice 9.5kB  lib/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js
npm notice 7.9kB  lib/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js.flow
npm notice 9.1kB  lib/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js
npm notice 7.6kB  lib/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js.flow
npm notice 720B   lib/generators/modules/GenerateModuleObjCpp/header/serializeStruct.js
npm notice 836B   lib/generators/modules/GenerateModuleObjCpp/header/serializeStruct.js.flow
npm notice 6.4kB  lib/generators/modules/GenerateModuleObjCpp/index.js
npm notice 6.6kB  lib/generators/modules/GenerateModuleObjCpp/index.js.flow
npm notice 14.6kB lib/generators/modules/GenerateModuleObjCpp/serializeMethod.js
npm notice 13.1kB lib/generators/modules/GenerateModuleObjCpp/serializeMethod.js.flow
npm notice 2.9kB  lib/generators/modules/GenerateModuleObjCpp/source/serializeModule.js
npm notice 3.6kB  lib/generators/modules/GenerateModuleObjCpp/source/serializeModule.js.flow
npm notice 6.8kB  lib/generators/modules/GenerateModuleObjCpp/StructCollector.js
npm notice 5.4kB  lib/generators/modules/GenerateModuleObjCpp/StructCollector.js.flow
npm notice 549B   lib/generators/modules/GenerateModuleObjCpp/Utils.js
npm notice 673B   lib/generators/modules/GenerateModuleObjCpp/Utils.js.flow
npm notice 848B   lib/generators/modules/Utils.js
npm notice 1.3kB  lib/generators/modules/Utils.js.flow
npm notice 7.1kB  lib/generators/RNCodegen.js
npm notice 8.4kB  lib/generators/RNCodegen.js.flow
npm notice 647B   lib/generators/Utils.js
npm notice 700B   lib/generators/Utils.js.flow
npm notice 14.1kB lib/parsers/flow/components/__test_fixtures__/failures.js
npm notice 14.1kB lib/parsers/flow/components/__test_fixtures__/failures.js.flow
npm notice 27.8kB lib/parsers/flow/components/__test_fixtures__/fixtures.js
npm notice 27.9kB lib/parsers/flow/components/__test_fixtures__/fixtures.js.flow
npm notice 2.6kB  lib/parsers/flow/components/commands.js
npm notice 2.9kB  lib/parsers/flow/components/commands.js.flow
npm notice 6.2kB  lib/parsers/flow/components/events.js
npm notice 6.6kB  lib/parsers/flow/components/events.js.flow
npm notice 1.2kB  lib/parsers/flow/components/extends.js
npm notice 1.6kB  lib/parsers/flow/components/extends.js.flow
npm notice 8.1kB  lib/parsers/flow/components/index.js
npm notice 6.5kB  lib/parsers/flow/components/index.js.flow
npm notice 1.8kB  lib/parsers/flow/components/options.js
npm notice 2.1kB  lib/parsers/flow/components/options.js.flow
npm notice 13.1kB lib/parsers/flow/components/props.js
npm notice 13.8kB lib/parsers/flow/components/props.js.flow
npm notice 2.1kB  lib/parsers/flow/components/schema.js
npm notice 1.3kB  lib/parsers/flow/components/schema.js.flow
npm notice 738B   lib/parsers/flow/errors.js
npm notice 849B   lib/parsers/flow/errors.js.flow
npm notice 5.7kB  lib/parsers/flow/index.js
npm notice 3.7kB  lib/parsers/flow/index.js.flow
npm notice 5.3kB  lib/parsers/flow/modules/__test_fixtures__/failures.js
npm notice 5.4kB  lib/parsers/flow/modules/__test_fixtures__/failures.js.flow
npm notice 15.4kB lib/parsers/flow/modules/__test_fixtures__/fixtures.js
npm notice 15.4kB lib/parsers/flow/modules/__test_fixtures__/fixtures.js.flow
npm notice 8.6kB  lib/parsers/flow/modules/errors.js
npm notice 9.5kB  lib/parsers/flow/modules/errors.js.flow
npm notice 25.7kB lib/parsers/flow/modules/index.js
npm notice 21.8kB lib/parsers/flow/modules/index.js.flow
npm notice 416B   lib/parsers/flow/modules/schema.js
npm notice 557B   lib/parsers/flow/modules/schema.js.flow
npm notice 595B   lib/parsers/flow/modules/utils.js
npm notice 830B   lib/parsers/flow/modules/utils.js.flow
npm notice 4.4kB  lib/parsers/flow/utils.js
npm notice 5.1kB  lib/parsers/flow/utils.js.flow
npm notice 428B   lib/parsers/schema/index.js
npm notice 526B   lib/parsers/schema/index.js.flow
npm notice 12.9kB lib/parsers/typescript/components/__test_fixtures__/failures.js
npm notice 13.0kB lib/parsers/typescript/components/__test_fixtures__/failures.js.flow
npm notice 28.1kB lib/parsers/typescript/components/__test_fixtures__/fixtures.js
npm notice 28.1kB lib/parsers/typescript/components/__test_fixtures__/fixtures.js.flow
npm notice 3.1kB  lib/parsers/typescript/components/commands.js
npm notice 3.0kB  lib/parsers/typescript/components/commands.js.flow
npm notice 7.0kB  lib/parsers/typescript/components/events.js
npm notice 7.4kB  lib/parsers/typescript/components/events.js.flow
npm notice 1.3kB  lib/parsers/typescript/components/extends.js
npm notice 1.6kB  lib/parsers/typescript/components/extends.js.flow
npm notice 8.2kB  lib/parsers/typescript/components/index.js
npm notice 6.6kB  lib/parsers/typescript/components/index.js.flow
npm notice 1.8kB  lib/parsers/typescript/components/options.js
npm notice 2.1kB  lib/parsers/typescript/components/options.js.flow
npm notice 19.4kB lib/parsers/typescript/components/props.js
npm notice 18.0kB lib/parsers/typescript/components/props.js.flow
npm notice 2.1kB  lib/parsers/typescript/components/schema.js
npm notice 1.3kB  lib/parsers/typescript/components/schema.js.flow
npm notice 738B   lib/parsers/typescript/errors.js
npm notice 849B   lib/parsers/typescript/errors.js.flow
npm notice 5.8kB  lib/parsers/typescript/index.js
npm notice 3.9kB  lib/parsers/typescript/index.js.flow
npm notice 4.7kB  lib/parsers/typescript/modules/__test_fixtures__/failures.js
npm notice 4.7kB  lib/parsers/typescript/modules/__test_fixtures__/failures.js.flow
npm notice 18.8kB lib/parsers/typescript/modules/__test_fixtures__/fixtures.js
npm notice 18.8kB lib/parsers/typescript/modules/__test_fixtures__/fixtures.js.flow
npm notice 8.6kB  lib/parsers/typescript/modules/errors.js
npm notice 9.5kB  lib/parsers/typescript/modules/errors.js.flow
npm notice 26.8kB lib/parsers/typescript/modules/index.js
npm notice 22.9kB lib/parsers/typescript/modules/index.js.flow
npm notice 416B   lib/parsers/typescript/modules/schema.js
npm notice 557B   lib/parsers/typescript/modules/schema.js.flow
npm notice 595B   lib/parsers/typescript/modules/utils.js
npm notice 830B   lib/parsers/typescript/modules/utils.js.flow
npm notice 4.6kB  lib/parsers/typescript/utils.js
npm notice 5.3kB  lib/parsers/typescript/utils.js.flow
npm notice 1.4kB  lib/SchemaValidator.js
npm notice 1.6kB  lib/SchemaValidator.js.flow
npm notice 1.4kB  package.json
npm notice === Tarball Details ===
npm notice name:          react-native-codegen
npm notice version:       0.70.1
npm notice filename:      react-native-codegen-0.70.1.tgz
npm notice package size:  168.4 kB
npm notice unpacked size: 1.3 MB
npm notice shasum:        10bf591db802342bd5ac38352821ad6452ba4b52
npm notice integrity:     sha512-KXRXARscSO4mt[...]WCnuO5sLFEYQg==
npm notice total files:   167
npm notice
+ react-native-codegen@0.70.1
```

Reviewed By: dmitryrykun

Differential Revision: D37851965

Pulled By: cortinico

fbshipit-source-id: 4d8c80831691e5f671c234bc3a1678ccb7435ff4
2022-07-27 16:18:33 +01:00
Distiller 72e1eda073 [0.68.2] Bump version numbers 2022-05-09 10:43:44 +00:00
Nicola Corti dfda480a98 Bump used version of react-native-codegen to 0.0.17 2022-05-09 11:32:37 +01:00
Nicola Corti a5ddc2e165 Bump react-native-codegen to 0.0.17 2022-05-09 11:30:22 +01:00
62ef6f5fa1 [Main][Windows] Working around Long paths limitation on Windows (#33707)
Co-authored-by: Aleš Pergl <alespergl@users.noreply.github.com>
Co-authored-by: Ales Pergl <alpergl@microsoft.com>
2022-05-09 11:21:43 +01:00
Distiller b5f1b26a8f [0.68.1] Bump version numbers 2022-04-13 13:37:50 +00:00
Marek Fořt 48113b5635 Merge pull request #33628 from fortmarek/fix/yarn-lock-gradle-plugin
Update yarn.lock with the new react-native-gradle-plugin version
2022-04-13 14:55:02 +02:00
fortmarek faaf2560cd Update yarn.lock with the new react-native-gradle-plugin version 2022-04-13 14:49:32 +02:00
Nicola Corti 387ee70e72 Use NDK 23 only for Windows users. (#33611)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/33611

Bumping the NDK to 23 to prevent build failures due to the NDK
using longer paths.

Changelog:
[Android] [Fixed] - Use NDK 23 only for Windows users.

Reviewed By: motiz88

Differential Revision: D35547459

fbshipit-source-id: 4e44c0b5fd8d1c559b04fb43eb4eeadc13943394
2022-04-13 14:43:44 +02:00
Nicola Corti 3fd3fe04da react-native-gradle-plugin should not depend on react-native-codegen NPM package
Summary:
This adds an unnecessary dependency between two NPM package which can be avoided. See https://github.com/reactwg/react-native-releases/discussions/17#discussioncomment-2452813
for context.

Changelog:
[Internal] [Changed] - react-native-gradle-plugin should not depend on react-native-codegen NPM package

Reviewed By: dmitryrykun

Differential Revision: D35279729

fbshipit-source-id: f18f79809f115f28203ac0a843fafead63528904
2022-04-13 14:34:08 +02:00
Danilo Bürger fdd7848454 Replaced windowsAwareYarn with windowsAwareCommandLine for node calls (#33530)
Summary:
It is not necessary to call node via yarn. Instead with this commit node is called directly (windows aware). This enables builds on systems that don't have yarn installed.

Fixes https://github.com/facebook/react-native/issues/33525

## Changelog

[Android] [Fixed] - Don't require yarn for codegen tasks

Pull Request resolved: https://github.com/facebook/react-native/pull/33530

Test Plan:
1. react-native init test
2. cd test
3. enable newArchEnabled=true (gradle.properties)
4. enable enableHermes: true (build.gradle)
5. react-native run-android (when the yarn is not installed on the system)

(I have not tested or verified if this works on windows build machines)

Reviewed By: sshic

Differential Revision: D35279376

Pulled By: cortinico

fbshipit-source-id: 430e4a7bcdec7d5377efac747f6b935d634451cc
2022-04-13 14:34:02 +02:00
Nicola Corti 1f48b7b0a2 Bump React Native Gradle plugin to 0.0.6 (#33581)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/33581

We currently have some code on the RN Gradle Plugin that we need to ship.
There are both bugfixes needed for RN 0.68.1 and for the current nightly
(therefore needed for RN 0.69).

I've verified that this works on a fresh RN 0.68.0 install with `npm pack`
and triggering a build on a fresh setup from app template, with newArchEnabled set to true.

Changelog:
[Android] [Changed] - Bump React Native Gradle plugin to 0.0.6

Reviewed By: rubennorte

Differential Revision: D35439444

fbshipit-source-id: 662b2211c44b261e3e3c9cddc946107cfb495c79
2022-04-13 14:33:24 +02:00
Nicola Corti 6268836c74 Improve support for Android users on M1 machine (#33588)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/33588

Currently users on M1 machine can't use the New Architecture correctly as they will get build failures when building the native code.

This Diff fixes it by automatically recognizing the host architecture and switching to NDK 24 if user is runnign on `aarch64`

Changelog:
[Android] [Fixed] - Improve support for Android users on M1 machine

Reviewed By: mdvacca

Differential Revision: D35468252

fbshipit-source-id: b73f5262b9408f04f3ae4fd26458a4d17c1ec29a
2022-04-13 14:30:30 +02:00
Danilo Bürger 9efcaffe17 Pin use-subscription to < 1.6.0 (#33541)
Summary:
Starting with 1.6.0 this package relies on react 18 which is currently not supported by rn

See https://github.com/facebook/react/blob/main/packages/use-subscription/package.json#L18

Fixes https://github.com/facebook/react-native/issues/33540

## Changelog

[General] [Fixed] - Pin use-subscription to < 1.6.0

Pull Request resolved: https://github.com/facebook/react-native/pull/33541

Test Plan:
Run `yarn` in the following directories:
`xplat/js/RKJSModules`
`xplat/js/react-native-github` (Identified from jest test)
`xplat/js/public` (Identified from yarn-validate)

`js1 build buckfiles` (From diff signals)

Reviewed By: yungsters

Differential Revision: D35333789

Pulled By: ryancat

fbshipit-source-id: bdb6232ccd2c5cece397f781609af2de7d36c5ff
2022-04-13 14:30:10 +02:00
Nicola Corti 8400590abe Template: Specify abiFilters if enableSeparateBuildPerCPUArchitecture is not set.
Summary:
As users can toggle `enableSeparateBuildPerCPUArchitecture` to create a split APK, once that is off, the `-PreactNativeArchitecture` is not correctly considered when building the local module.

This will make sure that, if users have `enableSeparateBuildPerCPUArchitecture` set to `false`, their
app is building the local `app_modules` only for the requested architectures.

Practically, users invoking with `--active-arch-only` might experience a build failure if they have a fully clean environment (would be forced to do a full build before using `--active-arch-only`). This addresses this scenario.

Changelog:
[Android] [Fixed] - Template: Specify abiFilters if enableSeparateBuildPerCPUArchitecture is not set.

Reviewed By: ShikaSD

Differential Revision: D35250700

fbshipit-source-id: 4e555888636cf182495fab2b4a562d93a70b9e66
2022-04-13 14:30:01 +02:00
Krzysztof Magiera a5c44e622e Fix dynamic_cast (RTTI) by adding key function to ShadowNodeWrapper and related classes (#33500)
Summary:
This PR fixes RTTI (run-time type information) for ShadowNodeWrapper and ShadowNodeListWrapper classes, i.e., calls to dynamic_cast and dynamic_pointer_cast that are called via JSI's getHostObject calls.

The fix is simply to add a so-called "key function" in a form of virtual destructor. Key functions needs to be a virtual non-pure and non-inlined functions that points the compiler as to which library contains the vtable/type information for a given class (see https://itanium-cxx-abi.github.io/cxx-abi/abi.html#vague-vtable and https://developer.android.com/ndk/guides/common-problems#rttiexceptions_not_working_across_library_boundaries)

Without the "key function", calls to dynamic_cast for ShadowNodeWrapper instances won't work across library boundaries because the class will have separate definitions in each separate library, therefore objects created in one of those libraries won't be recognized as the same type by the other library. This has been a problem in reanimated and gesture-handler libraries where we call `object.getHostObject<ShadowNodeWrapper>(rt)` (this is a method from JSI) in order to access ShadowNode instance from a handle we have in JS. I think, this issue is going to be relevant to more libraries that cope with view instances. In this scenario, we have a separate library, say "libreanimated.so" that calls to `getHostObject` which is an inline function that calls `dynamic_cast` for the `ShadowNodeWrapper` class. On the other hand, the instances of `ShadowNodeWrapper` are created by the code from `libreact_render_uimanager.so`. Because of that `dynamic_cast` fails even though it is called on instance of `ShadowNodeWrapper` because the class has separate vtable/type info: one in `libreanimated.so` and one in `libreact_render_uimanager.so` (by "fails" I mean that it actually returns `nullptr`).

This problem has been documented here: https://developer.android.com/ndk/guides/common-problems#rttiexceptions_not_working_across_library_boundaries where the solution is for the class to have a so-called "key function". The key function makes it so that compiler sees that one of the implementation for a given class is missing and therefore can safely assume that a vtable/type info for a given class is embedded into some library we link to.

This change adds a virtual destructor that is declared in the header file but defined in file that gets compiled as a part of `libreact_render_uimanager`. As a result, the compiler only creates one vtable/type info and calls to dynamic_cast works as expected in all libraries for `ShadowNodeWrapper` and `ShadowNodeListWrapper` classes.

This issue would only surface on Android, because on iOS all libraries by default are bundled together via Pods, whereas on Android each library is loaded separately using dynamic loading.

## Changelog

[Fabric][Android specific] - Fix dynamic_cast (RTTI) for ShadowNodeWrapper and similar classes when accessed by third-party libraries.

Pull Request resolved: https://github.com/facebook/react-native/pull/33500

Test Plan:
1. In order to test this you need to add a library that'd include  `<react/renderer/uimanager/primitives.h>` (i.e. use this branch of reanimated library: https://github.com/software-mansion/react-native-reanimated/tree/fabric)
2. After compiling the app inspect libreact_render_uimanager.so and libreanimated.so artifacts with `nm` tool
3. Notice that symbols like `vtable for facebook::react::ShadowNodeWrapper` and `typeinfo for facebook::react::ShadowNodeWrapper` are only present in the former and not in the latter library (before this change you'd see them both)

Reviewed By: ShikaSD

Differential Revision: D35143600

Pulled By: javache

fbshipit-source-id: 5fb25a02365b99a515edc81e5485a77017c56eb8
2022-04-13 14:29:45 +02:00
Marek Fořt 44ee801c29 Merge pull request #33582 from mganandraj/NewArchWinBuild
Building new architecture sources on Windows
2022-04-13 14:18:26 +02:00
Anandraj 5a8033df98 Fix for building new architecture sources on Windows
On Windows there are limits on number of character in file paths and in command lines
Ref: https://android.googlesource.com/platform/ndk/+/master/docs/BuildSystemMaintainers.md#Path-Length-Limits
NDK allows circumventing command line limits using response(RSP) files as inputs using NDK_APP_SHORT_COMMANDS flag.

Windows can support long file paths if configured through registry or by prefixing all file paths with a special character sequence
The latter requires changes in NDK. And there are tools in NDK (AR) which is not able to handle long paths (>256) even after setting the registry key.
The new architecutre source tree is too deep, and the object file naming conventions in NDK makes the matters worse, by producing incredibly long file paths.
Other solutions such as symlinking source code etc. didn't work as expected, and makes the build scripts complicated and hard to manage.
This change temporarily works around the issue by placing the temporary build outputs as short a path as possible within the project path.

Changelog:
[Android] [Fixed] - Fix for building new architecture sources on Windows
2022-04-12 12:46:05 +01:00
Distiller 51f5ea15aa [0.68.0] Bump version numbers 2022-03-30 14:40:30 +00:00
Distiller a4a6e23dec [0.68.0-rc.4] Bump version numbers 2022-03-25 14:43:18 +00:00
Riccardo Cipolleschi e645629323 Enable SonarKit and Flipper in React-Core (#33499)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/33499

This DIFF turns on the `FB_SONARKIT_ENABLED` flag when installing Flipper ina RN app. The flag is enabled only in Debug config, given that Flipper is installed only in this configuration.

This PR also fixes this issue: https://github.com/facebook/react-native/issues/33497

This PR is required because release 0.67 has the Flag in the app, while release 0.68 moved it in the React-Core pod.
We can't enable the flag at the `React-Core.podspec` level because we should not make assumptions on whether users want flipper or not.

## Changelog
[iOS][Changed] - Enable SonarKit in React-Core when the configuration is `'Debug'`

Reviewed By: cortinico

Differential Revision: D35141506

fbshipit-source-id: 171b7fa8ea7727c633ef963408e86b332c32e9fa
2022-03-25 14:34:47 +00:00
Distiller b3f19d7f1f [0.68.0-rc.3] Bump version numbers 2022-03-17 12:54:31 +00:00
Andrei Shikov cb28a269f0 Bump Flipper-Glog to 0.5.0.4
Summary:
Updates Flipper-Glog to address failing assertion in `mutex.h`

Changelog: [iOS][Updated] - Updated Flipper-Glog to 0.5.0.4

Reviewed By: bvanderhoof

Differential Revision: D34935757

fbshipit-source-id: 6e2dc5bf5608eb0d4cd89418fe1e04b8780bca16
2022-03-17 12:27:54 +00:00
Mike Hardy 4163386aee fix(ios, flipper): update flipper sub-pods to support macCatalyst (#33406)
Summary:
Flipper-DoubleConversion and Flipper-Glog iOS pods received a build optimization
a few versions back that pre-compiled the pods and references the xcframework slices

Unfortunately, the pre-compile did not include macCatalyst slices, so this disabled support
for flipper on macOS for react-native >0.65

lblasa has re-compiled the pods with the macCatalyst slices added

See https://github.com/facebook/flipper/issues/3117

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->

[iOS] [Fixed] - update Flipper pods to support re-enable macCatalyst

Pull Request resolved: https://github.com/facebook/react-native/pull/33406

Test Plan:
- [ ] The Flipper repo has a react-native test that appeared to work with these versions, CI should work here
- [ ] Prove there is no regression, a flipper-enabled build test should work for simulator iOS target on arm64
- [ ] Prove there is no regression, a flipper-enabled build test should work for simulator iOS target on x86_64 mac
- [ ] Prove there is no regression, a flipper-enabled build test should work for real device iOS target
- [ ] To prove the issue is resolved, a build should be attempted for a macCatalyst target, and it should work.

Reviewed By: cortinico

Differential Revision: D34789654

Pulled By: lblasa

fbshipit-source-id: 466803dd07b5820220512b7d7d760b94b8aa65f7
2022-03-17 12:26:36 +00:00
Danilo Bürger ccd1708096 Re-apply: Consider relative to pwd installation root when looking for files in rn module via cocoapods (#33427)
Summary:
This re-applies Consider relative to pwd installation root when looking for files in rn module via cocoapods by danilobuerger

The `:reactNativePath` provided by `use_native_modules!` is the rn module path relative to the installation root (usually `./ios`). However, when executing cocoapods from a dir thats not the installation root, packages that use the relative `:reactNativePath` variable in their path must also consider the relative to pwd installation root.

This fixes usage of cocoapods with the `--project-directory` flag like

```bash
bundle exec pod install --project-directory=ios
```
## Changelog

[iOS] [Fixed] - Fix usage of cocoapods with --project-directory flag and new arch

Pull Request resolved: https://github.com/facebook/react-native/pull/33427

Test Plan:
1) Enable the new arch
2) Execute from the projects root dir

```bash
bundle exec pod install --project-directory=ios
```

3) It will fail with

```
[!] Invalid `Podfile` file: [codegen] Couldn't not find react-native-codegen..
```

4) Apply the patch
5) Execute from the projects root dir

```bash
bundle exec pod install --project-directory=ios
```

6) It will succeed

Reviewed By: cortinico

Differential Revision: D34890926

Pulled By: dmitryrykun

fbshipit-source-id: f7adc6196874822d5ff38f275414f529d385f2ea
2022-03-16 18:40:11 +00:00
Krisztiaan 4e34e6c4fb fix: cliPath should handle absolute paths (#32983)
Summary:
Avoid breaking tools relying on absolute path for `cliPath`

## Changelog

[Android] [Fixed] - Enable cliPath to have an absolute path value

Pull Request resolved: https://github.com/facebook/react-native/pull/32983

Test Plan:
declare `cliPath` from `expo`:
```groovy
cliPath: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/cli.js",
```
and run an android build

Reviewed By: ShikaSD

Differential Revision: D33843275

Pulled By: cortinico

fbshipit-source-id: 65f55a5e07a4ec0a6205d5f06f150377708c30cc
2022-03-16 18:39:31 +00:00
Kudo Chien 3e69592c5a Fix ios build error when use_frameworks is on and fabric is off (v2) (#33409)
Summary:
alternative solution for https://github.com/facebook/react-native/issues/33379

> when `use_frameworks!` is on, there are errors like:
> ```
> 'FBReactNativeSpec/FBReactNativeSpec.h' file not found
> #import <FBReactNativeSpec/FBReactNativeSpec.h>
> ```
> this error may come from from https://github.com/facebook/react-native/commit/f7e4c07c84b6 regression.
>
> when `use_frameworks!` is on, xcode will search headers from framework directories, the correct imports would be `#import <React_Codegen/FBReactNativeSpec/FBReactNativeSpec.h>` (xcode will transform dash to underscore, so it is `React_Codegen` but not `React-Codegen`). in the other hand, when `use_frameworks!` is off, the correct import is `#import <React-Codegen/FBReactNativeSpec/FBReactNativeSpec.h>`.
>
>
> this fix is specific for old architecture (fabric is off).
>
> when fabric is on, there are other errors from duplicated headers when copying to build folder. [the reason is that framework build would try to flatten headers](https://mkonrad.net/2015/03/29/xcode-static-libraries-preserving-header-directory-structure.html). we have `primitives.h` in different folders and they would be flattened into `React_Fabric.framework/Headers`. to be honest, i don't know how to deal with the problem in the meantime,  maybe subspecs are not enough, we should separate them from subspecs to dedicated podspecs so that we can have these targets as different frameworks.

in this alternative fix, i try to add `React-Codegen/React_Codegen.framework/Headers` into header search paths and make original `#import <FBReactNativeSpec/FBReactNativeSpec.h>` reachable.

[this change](https://github.com/facebook/react-native/commit/7a0398c331f22abc619a64b444ec7153357b0a30) in the pr is just a workaround to solve breaking in latest main branch and this is not important to the `use_frameworks!` fix at all. this breaking was coming from https://github.com/facebook/react-native/commit/180495159517dc0bfa103621e5ff62fc04cb3c8b.

[iOS] [Fixed] - Fix iOS build error when Podfile `use_frameworks!` is on and Fabric is off

Pull Request resolved: https://github.com/facebook/react-native/pull/33409

Test Plan:
verify with rn-tester
1. change `fabric_enabled` to false in `packages/rn-tester/Podfile`
2. `USE_FRAMEWORKS=1 pod install`
3. build rn-tester in xcode

Reviewed By: dmitryrykun

Differential Revision: D34817041

Pulled By: cortinico

fbshipit-source-id: 4d1a610e99a807793eb3f64461e0d735c0a9ca9c
2022-03-16 18:39:15 +00:00
Danilo Bürger 5fd04faf1e Start surface after setting the delegate (#33402)
Summary:
When starting the surface, _propagateStageChange is called. This checks the delegate to call surface:didChangeStage: on it.

When initWithSurface:sizeMeasureMode: is called after start, then the delegate will be nil and thus not be called.

This turns it around so a delegate is present for the surface to propagate its state to.

This fixes RCTContentDidAppearNotification not getting posted otherwise.

## Changelog

[iOS] [Fixed] - Post RCTContentDidAppearNotification with new arch

Pull Request resolved: https://github.com/facebook/react-native/pull/33402

Test Plan:
I found it best to set a breakpoint in XCode to where RCTContentDidAppearNotification is being posted.

Prior to the patch that breakpoint will not be called. After applying the patch, it will be called.

Reviewed By: philIip

Differential Revision: D34753329

Pulled By: ShikaSD

fbshipit-source-id: cc44a4c3a787d49e22e9d0c3a82c0f11ed281a0a
2022-03-16 18:36:46 +00:00
Danilo Bürger a761b79ad8 Use relative installation root instead of absolute to avoid embedding absolute paths in pods project (#33187)
Summary:
Use relative installation root instead of absolute to avoid embedding absolute paths in pods project
Also removes a leading space from each path.

Before:

<img width="799" alt="155846827-94c474b7-8a79-45fc-a900-8860a94fb318" src="https://user-images.githubusercontent.com/996231/155847731-de128759-bff5-4d1f-a59a-377298055d85.png">

After:

<img width="745" alt="Screenshot 2022-02-26 at 15 58 32" src="https://user-images.githubusercontent.com/996231/155847739-b783debc-a805-4ce7-a88a-33f764dc5985.png">

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->

[iOS] [Fixed] - Remove absolute paths from pods project

Pull Request resolved: https://github.com/facebook/react-native/pull/33187

Test Plan: Pod install and view in Xcode FBReactNativeSpec -> Build Phases -> [CP-User] Generate Specs

Reviewed By: ShikaSD

Differential Revision: D34549541

Pulled By: dmitryrykun

fbshipit-source-id: 2926b093fb87f50ef9988e23fce593348f00077d
2022-03-16 18:36:40 +00:00
David Vacca d7d33c7ef3 Revert D31628461
Summary:
Revert of D31628461 (https://github.com/facebook/react-native/commit/e494e4beb6a124008fd116178cbc38335bd87809), this diff broke Text selections in Android. Also I'm unable to reproduce original bug fixed by D31628461 (https://github.com/facebook/react-native/commit/e494e4beb6a124008fd116178cbc38335bd87809)

changelog: [Android][Fixed] Fixed text selection, broken in 0.68

Reviewed By: ShikaSD

Differential Revision: D34700739

fbshipit-source-id: 85cde0032632dc77eb77f91b63c5d3c29b1bbbaf
2022-03-16 18:36:33 +00:00
David Vacca 7b33f20546 Ship enableNestedTextOnPressEventFix
Summary:
Ship the NestedTextOnPressEventFix feature flag

changelog: [internal] internal

Reviewed By: javache

Differential Revision: D34554304

fbshipit-source-id: 3350c56d0cc4f2593645bcb6650034be336bd902
2022-03-16 18:36:13 +00:00
Danilo Bürger 4b370ceeb7 Include folly fabric headers in source files (#33188)
Summary:
See the main spec preserve_paths, they are all also included as source_files. Without this, the folly fabric header files won't be available for other pods.

## Changelog

[iOS] [Fixed] - Include folly fabric headers in source files

Pull Request resolved: https://github.com/facebook/react-native/pull/33188

Test Plan:
Include react-native-safe-area-context 4.0.1 with new architecture active. It will result in a xcode build error:

<img width="466" alt="Screenshot 2022-02-26 at 19 33 35" src="https://user-images.githubusercontent.com/996231/155855017-8ea5f473-42a0-4e56-8108-472e6224ae7e.png">

Reviewed By: cortinico

Differential Revision: D34518841

Pulled By: motiz88

fbshipit-source-id: 1053fe204331a314427c793f28b2458e7b78e6c5
2022-03-16 18:34:31 +00:00
jonathanmos 63e4bd655a Fix Switch causing RetryableMountingLayerException (#32602)
Summary:
Added a null check to native.value in Switch to fix regression from RN 66 -> stuck operation in mViewCommandOperations list in Android Release on initial layout of a screen with Switch component. If approved, please incorporate this fix into an RN 66 release.

## Changelog
[Android][Fixed] - Added a null check to native.value in Switch to fix https://github.com/facebook/react-native/issues/32594

Pull Request resolved: https://github.com/facebook/react-native/pull/32602

Test Plan: To reproduce, put a log in UIViewOperationQueue in dispatchViewUpdates you can see that the RetryableMountingException is no longer thrown for a screen with the Switch component on Android Release. As a result, mViewCommandOperations no longer has a stuck operation on initial layout.

Reviewed By: charlesbdudley

Differential Revision: D34397788

Pulled By: lunaleaps

fbshipit-source-id: 1cee3516fb987942dfa50ad1f2d11c965a947f05
2022-03-16 18:34:21 +00:00
Nicola Corti 762db49ded Make sure configureNdkBuild* tasks are depending on preBuild
Summary:
Due to a bug with AGP, the configureNdkBuild* tasks are not depending on
preBuild tasks. We need to manually fix this otherwise the build will fail the
first time the NDK is invoked.

More on this here: https://issuetracker.google.com/issues/207403732

Changelog:
[Android] [Fixed] - Make sure configureNdkBuild* tasks are depending on
preBuild in the Android template.

Reviewed By: passy

Differential Revision: D34578860

fbshipit-source-id: 5d4caa7d2b7a976b1c9caa6dce8e186c1486cafa
2022-03-16 18:34:11 +00:00
Distiller 65e4d98a71 [0.68.0-rc.2] Bump version numbers 2022-02-24 17:17:14 +00:00
Lorenzo Sciandra 66b82aec55 chore(deps): bump CLI version to 7.0.3 to address web debugging issue (#33156)
Summary:
Doing this patch level bump to ensure that all packages consuming `react-native` will get `7.0.3` and not lower 7.x versions.
This is because this new patch contains this fix:  https://github.com/react-native-community/cli/pull/1560
(thanks NickGerleman & thymikee for your work!)

We'll have to cherry-pick this into the 0.68 branch.

While at it, I've also done a cheeky `npx yarn-deduplicate` to clean up the `yarn.lock` a bit.

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->

[General] [Changed] - Bump RN CLI to v7.0.3 to address web debugging issue

Pull Request resolved: https://github.com/facebook/react-native/pull/33156

Test Plan:
CI + some local testing via `test-manual-e2e`:
<img width="1779" alt="Screenshot 2022-02-21 at 11 40 54" src="https://user-images.githubusercontent.com/16104054/154948695-8c40bb56-87eb-4326-a740-49930994c08b.png">

Reviewed By: cortinico

Differential Revision: D34385503

Pulled By: motiz88

fbshipit-source-id: f0d8c4e0e92f83c0d819eeaa0fbec27820145968
2022-02-24 14:37:14 +00:00
Andrei Shikov 2cd3173991 Remove optional codegen config inside template (#33108)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/33108

The codegen config is optional and can be removed from the default package.json configuration in the template to simplify 0.68 upgrade for people who are not opted-in to the new arch.

Changelog: [Internal] - Remove optional codegenConfig field from template

Reviewed By: cortinico

Differential Revision: D34216988

fbshipit-source-id: 5c448472eed99bc112aef204c4025454171a83c5
2022-02-24 14:36:05 +00:00
Nicola Corti 61f0d1a7d1 Release react-native-gradle-plugin 0.0.5
Summary:
Time to release a new version of the Gradle plugin and bump it to 0.0.5
This version includes a deprecation of `reactRoot` and a warning message
to migrate to `root`/`reactNativeDir`.

Changelog:
[Android] [Changed] - Release react-native-gradle-plugin 0.0.5

Reviewed By: ShikaSD

Differential Revision: D34339272

fbshipit-source-id: c03495bda7c4185d674761e58a94fde8612f2941
2022-02-24 14:35:57 +00:00
Nicola Corti 57cc524e93 Gradle: Deprecate reactRoot in favor of root and reactNativeDir (#33142)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/33142

The `reactRoot` property was confusing as we were using it for both the root of the project
and the react-native NPM Package root. I'm deprecating it and splitting it in two.

I've added several warning in the codebase to tell the people how to migrate away from it.
Moreover this is specifying default values that are more user-friendly. Users won't have to
configure anything unless they are in a monorepo.

Changelog:
[Android] [Changed] - Gradle: Deprecate `reactRoot` in favor of `root` and `reactNativeDir`

Reviewed By: ShikaSD

Differential Revision: D34277050

fbshipit-source-id: fc7f45017452b086726516a9586cacd9a661c287
2022-02-24 14:35:45 +00:00
Nicola Corti bca4cf023a Set a resolution strategy for com.facebook.react:react-native when on New Architecture. (#33134)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/33134

When a user is enabling New Architecture, we should make sure they don't accidentally mix
imports of React Native from source vs prebuilts.

With this resolution strategy, we'll make sure all the import of `com.facebook.react:react-native:+`
will be resolved to the correct dependency.

Changelog:
[Android] [Fixed] - Set a resolution strategy for com.facebook.react:react-native when on New Architecture

Reviewed By: ShikaSD

Differential Revision: D34303267

fbshipit-source-id: 492fec59175c5887571e1b09ca8e233584b45dd1
2022-02-24 14:35:36 +00:00
Nicola Corti f5d371a2f9 Set Java source/target compatibility for react-native-gradle-plugin to 8
Summary:
This is necessary otherwise when building from source on JVM < 11, the `compileJava`
task of the Gradle Plugin will fail with `invalid source: 11`.
Essentially the Gradle build will not even start because of this. Instead we delegate
to a better formatted warning from either AGP or from our plugin.

Changelog:
[Internal] [Changed] - Set Java source/target compatibility for react-native-gradle-plugin to 8

Reviewed By: ShikaSD

Differential Revision: D34111799

fbshipit-source-id: 57ab11fe6c4532576776b586f75e8fcb5c71adcd
2022-02-24 14:35:00 +00:00
Muhammad Numan b1c30f8f8a fix: jvm 11 error message from ReactPlugin.kt and react.gradle (#33048)
Summary:
you can see discussion here: https://github.com/reactwg/react-native-releases/discussions/13#discussioncomment-2069527
we were getting this error message when we build Gradle with other than 11 JVM
```
> Task :react-native-gradle-plugin:compileJava FAILED
2 actionable tasks: 2 executed

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':react-native-gradle-plugin:compileJava'.
> invalid source release: 11
```
this solution is suggested by mikehardy

after this PR, now the error is like this
```
**************************************************************************************************************

ERROR: requires JDK11 or higher.
Incompatible major version detected: '8'

**************************************************************************************************************
```

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->

[Android] [Fixed] - jvm 11 error message

Pull Request resolved: https://github.com/facebook/react-native/pull/33048

Test Plan: install other than 11 java version and just run `./scripts/test-manual-e2e.sh` this command at the root of RN repo than this error will appair `invalid source release: 11`

Reviewed By: ShikaSD

Differential Revision: D34110990

Pulled By: cortinico

fbshipit-source-id: c142a363c7cec0db65d5ab9da858fd25866c7c49
2022-02-24 14:34:44 +00:00
Alfonso Curbelo 54f5c8c26c Fixed - Text with adjustsFontSizeToFit changes the text layout infinitely (#33135)
Summary:
Fixes the infinite loop explained in the issue https://github.com/facebook/react-native/issues/33129 by reverting commit  59021521e7. PR https://github.com/facebook/react-native/pull/31538.

`onCollectExtraUpdates` is part of the node update cycle. By marking the node as updated `markUpdated()` in `onCollectExtraUpdates` we are restarting the update infinitely.

Unfortunately, reverting this PR also reintroduces the original issue https://github.com/facebook/react-native/issues/30717 which IMO is minor compared to the infinite loop.

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->

[Android] [Fixed] - Text with adjustsFontSizeToFit changes the text layout infinitely

Pull Request resolved: https://github.com/facebook/react-native/pull/33135

Test Plan:
I added a console.log to the Text `onTextLayout` in [packages/rn-tester/js/examples/Text/TextAdjustsDynamicLayoutExample.js ](https://github.com/facebook/react-native/blob/main/packages/rn-tester/js/examples/Text/TextAdjustsDynamicLayoutExample.js) to see if the infinite loop is gone.

![image](https://user-images.githubusercontent.com/3791120/154523914-e6aa7cf5-7a1c-488f-a392-898f4c85a833.png)

![Screen Shot 2022-02-17 at 11 20 31 AM](https://user-images.githubusercontent.com/3791120/154524274-880c3bed-d2c6-456b-8947-42e75793c424.jpg)

```

Reviewed By: ShikaSD

Differential Revision: D34310218

Pulled By: lunaleaps

fbshipit-source-id: 0d40f49d15c562ec25983145897bd95dc182f897
2022-02-24 14:12:49 +00:00
Minsik Kim 48f9733867 Fix a broken input for the Korean alphabet in TextInput (#32523)
Summary:
<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->

Fix https://github.com/facebook/react-native/issues/32503

Updating the attributed text in TextView/TextField while inputting Korean language will break input mechanism of the Korean alphabet. This results unexpected text input.

This PR supersedes the previous fixes: https://github.com/facebook/react-native/issues/19809, https://github.com/facebook/react-native/issues/22546

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->

[iOS] [Fixed] - Fix a broken input for the Korean alphabet in TextInput

Pull Request resolved: https://github.com/facebook/react-native/pull/32523

Test Plan: https://user-images.githubusercontent.com/20317121/140013434-1674c391-54d6-4410-b4c1-c633697e639d.mov

Reviewed By: lunaleaps, sammy-SC

Differential Revision: D32470543

Pulled By: philIip

fbshipit-source-id: e7e34bd362fa2ab2ca579103db01ad8d1a891c35
2022-02-24 14:12:39 +00:00
Nicola Corti 43d67cd1ec Remove react-native-gradle-plugin as a dependency from template's package.json
Summary:
We should now be able to remove the explicit `react-native-gradle-plugin` dependency
from the `template/package.json` file.

Changelog:
[Android] [Changed] - Remove `react-native-gradle-plugin` as a dependency from template's package.json

Reviewed By: motiz88

Differential Revision: D34050589

fbshipit-source-id: c8d4e4fba481af6b56723906b71411132d60aded
2022-02-24 14:12:28 +00:00
Nicola Corti 805afe4847 Make react-native depend on react-native-gradle-plugin
Summary:
Similarly to what we did for react-native-codegen, I'm introducing
a dependency between RN and the Gradle plugin, to be processed upon OSS bumps.

Changelog:
[General] [Added] - Make react-native depend on react-native-gradle-plugin

Reviewed By: motiz88

Differential Revision: D31334773

fbshipit-source-id: 978da4946b7864d891553e6a7dcb67783399e76f
2022-02-24 14:12:19 +00:00
Nicola Corti 0924a28b19 Update test_android_template to use build_npm_package (#33068)
Summary:
I'm updating the `test_android_template` CI step to use the result of `build_npm_package` instead of sed-ing the RN version to `file:..`.
This should be more robust and will allow to install transitive deps automatically, without having to deal with separate installation steps.

This also fixes the broken CI for Android

Changelog:
[Internal] [Changed] - Update `test_android_template` to use `build_npm_package`

Pull Request resolved: https://github.com/facebook/react-native/pull/33068

Reviewed By: ShikaSD

Differential Revision: D34083047

Pulled By: cortinico

fbshipit-source-id: de34472d5737db445cfc0d4b1c6feaf1e746bb61
2022-02-24 14:12:05 +00:00
Chiara Mooney 1b2b2198e1 Backport ReactCommon Fix (#33086)
[General] [Fixed] - Fix build break on Windows with ReactCommon
2022-02-18 16:07:04 -08:00
Distiller 319e4df458 [0.68.0-rc.1] Bump version numbers 2022-02-03 17:23:36 +00:00
Nicola Corti 71548ffd69 Do not bundle libhermes.so or libjsc.so inside the React Native Android AAR (#33038)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/33038

While rolling out RN 0.68.x we noticed that `libhermes.so` and `libjsc.so` were included
inside the final .aar we publish to NPM. This forced users (on both old or new arch) to
specify a `pickFirst` directive inside their packaging option (which is unpractical and
risky as the two .so might not be compatible each other if they're coming from
different Hermes/JSC versions).

Changelog:
[Android] [Fixed] - Do not bundle libhermes.so or libjsc.so inside the React Native Android AAR

Reviewed By: ShikaSD

Differential Revision: D33979107

fbshipit-source-id: 0b71d59f210b8bc9903cd0f30ed6e2120aab99e0
2022-02-03 17:12:54 +00:00
Elias Nahum 58eb8b13a0 Android: Fix crash when WindowInsets is null on ReactRootView (#32989)
Summary:
Fixes a potential crash was introduced by https://github.com/facebook/react-native/issues/30919 that aimed to get the keyboard height on devices with a Notch. The problem is that it considers that any ReactRootView will have an insets available.

When using [react-native-navigation](https://github.com/wix/react-native-navigation) and assigning a Navigation button to the TopBar as a component, the component gets registered as a RootView but won't have any insets attach to the view.

[getRootWindowInsets()](https://developer.android.com/reference/android/view/View#getRootWindowInsets()) in fact return a `WindowInset` only available if the view is attached, so when executing `checkForKeyboardEvents` method from ReactRootView, is trying to access the `DisplayCutout` of a null object, leading to a crash.

## Changelog

[Android] [Fixed] - Fix potential crash if ReactRootView does not have insets attached.

Pull Request resolved: https://github.com/facebook/react-native/pull/32989

Test Plan:
Without the code change: Notice how the second screen being push contains a React Component on the top right of the navigation bar, and when component is unmounted (going back) the app crashes.

https://user-images.githubusercontent.com/6757047/151558235-39b9a8b5-be73-4c31-8053-02ce188637b8.mp4

crash log
```
2022-01-28 10:27:52.902 15600-15600/com.mattermost.rnbeta E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.mattermost.rnbeta, PID: 15600
    java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.DisplayCutout android.view.WindowInsets.getDisplayCutout()' on a null object reference
        at com.facebook.react.ReactRootView$CustomGlobalLayoutListener.checkForKeyboardEvents(ReactRootView.java:778)
        at com.facebook.react.ReactRootView$CustomGlobalLayoutListener.onGlobalLayout(ReactRootView.java:769)
        at android.view.ViewTreeObserver.dispatchOnGlobalLayout(ViewTreeObserver.java:1061)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3214)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2143)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8665)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1037)
        at android.view.Choreographer.doCallbacks(Choreographer.java:845)
        at android.view.Choreographer.doFrame(Choreographer.java:780)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1022)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7839)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
```

After applying the patch which is only a null check validation and does not change any previous behavior

https://user-images.githubusercontent.com/6757047/151558429-9ff1a608-abb6-4168-8db9-df0c3c71d79e.mp4

Reviewed By: cortinico

Differential Revision: D33844955

Pulled By: ShikaSD

fbshipit-source-id: ed5579ad3afeed009c61cc1851eee45c70087cf5
2022-02-03 17:12:38 +00:00
Andrei Shikov 5ba5ff6eba Cleanup OSS proguard rules and add @DoNotStripAny
Summary:
title

Changelog: [Changed][Android] - Added DoNotStripAny proguard rules

Reviewed By: cortinico

Differential Revision: D33921797

fbshipit-source-id: 93628d6222c74976b40efc2507a482d8a6a4fe1b
2022-02-03 17:12:23 +00:00
Andrei Shikov a2a7032477 Increase max heap size for template gradle build
Summary:
Updates maximum heap size for the gradle build to account for building RN from source when new architecture is enabled.

Changelog: [Changed][Android] - Use 2g as a default heap size for gradle builds

Reviewed By: cortinico

Differential Revision: D33947090

fbshipit-source-id: 2f551e688f2d92c3092e053086f6933779cd6f63
2022-02-03 17:12:06 +00:00
Lorenzo Sciandra f06df0ef2b chore(deps): bump CLI version to 7 to get new version of Metro (67) (#33019)
Summary:
This PR bumps the CLI dependency to v7, which is a new version made so that we can obtain the new version of Metro via https://github.com/react-native-community/cli/commit/b53ba5b0d6091a11985e3f6821a085e5d36cf08f

After merging this, we should cherry pick this commit in the 0.68 branch and do a new RC

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->

[General] [Changed] - Bump RN CLI to v7, and Metro to 67

Pull Request resolved: https://github.com/facebook/react-native/pull/33019

Test Plan:
Tried some quick local testing with test-manual-e2e, seems to be working fine:

<img width="929" alt="Screenshot 2022-02-01 at 16 38 01" src="https://user-images.githubusercontent.com/16104054/152010647-f0a7a803-6e46-41d4-91fc-5f9d6753f88e.png">

Reviewed By: cortinico

Differential Revision: D33918810

Pulled By: ShikaSD

fbshipit-source-id: 28ff52c4c89b5ca2390527aa6c66fb2ce236316e
2022-02-03 17:11:00 +00:00
Distiller 0fd6ade862 [0.68.0-rc.0] Bump version numbers 2022-01-28 19:26:26 +00:00
5456 changed files with 257197 additions and 264660 deletions
-8
View File
@@ -1,8 +0,0 @@
{
"presets": [
"module:metro-react-native-babel-preset"
],
"plugins": [
"babel-plugin-transform-flow-enums"
]
}
-7
View File
@@ -2,10 +2,6 @@
[android]
target = android-31
[kotlin]
compile_against_abis = True
kotlin_version = 1.6.10
[download]
max_number_of_retries = 3
@@ -15,6 +11,3 @@
[alias]
rntester = //packages/rn-tester/android/app:app
[buildfile]
includes = //tools/build_defs/oss/preload.bzl
+19 -9
View File
@@ -14,31 +14,41 @@
# and build a Android application that can be used to run the
# tests specified in the scripts/ directory.
#
FROM reactnativecommunity/react-native-android:7.0
FROM reactnativecommunity/react-native-android:5.2
LABEL Description="React Native Android Test Image"
LABEL maintainer="Héctor Ramos <hector@fb.com>"
# set default environment variables
ENV GRADLE_OPTS="-Dorg.gradle.daemon=false -Dfile.encoding=utf-8 -Dorg.gradle.jvmargs=\"-Xmx512m -XX:+HeapDumpOnOutOfMemoryError\""
ENV KOTLIN_HOME="packages/react-native/third-party/kotlin"
ENV GRADLE_OPTS="-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs=\"-Xmx512m -XX:+HeapDumpOnOutOfMemoryError\""
ENV JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF8"
ADD .buckconfig /app/.buckconfig
ADD .buckjavaargs /app/.buckjavaargs
ADD BUCK /app/BUCK
ADD packages/react-native/Libraries /app/packages/react-native/Libraries
ADD packages/react-native/ReactAndroid /app/packages/react-native/ReactAndroid
ADD packages/react-native/ReactCommon /app/packages/react-native/ReactCommon
ADD packages/react-native/React /app/packages/react-native/React
ADD Libraries /app/Libraries
ADD ReactAndroid /app/ReactAndroid
ADD ReactCommon /app/ReactCommon
ADD React /app/React
ADD keystores /app/keystores
ADD packages/react-native-codegen /app/packages/react-native-codegen
ADD tools /app/tools
ADD scripts /app/scripts
WORKDIR /app
RUN buck fetch ReactAndroid/src/test/java/com/facebook/react/modules
RUN buck fetch ReactAndroid/src/main/java/com/facebook/react
RUN buck fetch ReactAndroid/src/main/java/com/facebook/react/shell
RUN buck fetch ReactAndroid/src/test/...
RUN buck fetch ReactAndroid/src/androidTest/...
RUN buck build ReactAndroid/src/main/java/com/facebook/react
RUN buck build ReactAndroid/src/main/java/com/facebook/react/shell
ADD . /app
RUN yarn
RUN ./gradlew :packages:react-native:ReactAndroid:assembleDebug
RUN ./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog
RUN ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=1
@@ -33,7 +33,7 @@ node cli.js bundle --platform android --dev true --entry-file ReactAndroid/src/a
# build test APK
# shellcheck disable=SC1091
source ./scripts/android-setup.sh && NO_BUCKD=1 scripts/retry3 buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=1
source ./scripts/android-setup.sh && NO_BUCKD=1 retry3 buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=1
# run installed apk with tests
node ./.circleci/Dockerfiles/scripts/run-android-ci-instrumentation-tests.js "$*"
@@ -141,7 +141,7 @@ function e2e_suite() {
fi
fi
if ! (cd packages/react-native && FILENAME=$(npm pack | tail -1) && mv "$FILENAME" ../../)
if ! npm pack
then
echo "Failed to pack react-native"
return 1
+344 -1367
View File
File diff suppressed because it is too large Load Diff
-36
View File
@@ -1,36 +0,0 @@
storage: ./storage
auth:
htpasswd:
file: ./htpasswd
uplinks:
npmjs:
url: https://registry.npmjs.org/
max_fails: 40
maxage: 30m
timeout: 60s
fail_timeout: 10m
cache: false
agent_options:
keepAlive: true
maxSockets: 40
maxFreeSockets: 10
packages:
# Get @react-native/normalize-color from npm registry, since its used in deprecated-react-native-prop-types package
'@react-native/normalize-color':
access: $all
publish: $authenticated
proxy: npmjs
# Group and isolate all local packages, avoid being proxy from outside
'@react-native/*':
access: $all
publish: $all
'@*/*':
access: $all
publish: $authenticated
proxy: npmjs
'**':
access: $all
publish: $all
proxy: npmjs
logs:
- {type: file, path: verdaccio.log, format: json, level: warn}
+4 -4
View File
@@ -1,10 +1,10 @@
**/main.js
**/staticBundle.js
bots/node_modules
docs/generatedComponentApiDocs.js
packages/react-native/flow/
packages/react-native/Libraries/Renderer/*
packages/react-native/Libraries/vendor/**/*
flow/
Libraries/Renderer/*
Libraries/vendor/**/*
node_modules/
packages/*/node_modules
packages/react-native-codegen/lib
tools/eslint/rules/sort-imports.js
+60
View File
@@ -0,0 +1,60 @@
{
"root": true,
"extends": [
"./packages/eslint-config-react-native-community/index.js"
],
"plugins": [
"@react-native/eslint-plugin-specs"
],
"overrides": [
{
"files": [
"Libraries/**/*.js",
],
"rules": {
"@react-native-community/no-haste-imports": 2,
"@react-native-community/error-subclass-name": 2,
"@react-native-community/platform-colors": 2,
"@react-native/specs/react-native-modules": 2
}
},
{
"files": [
"flow-typed/**/*.js",
],
"rules": {
quotes: 0
}
},
{
"files": [
"**/__fixtures__/**/*.js",
"**/__mocks__/**/*.js",
"**/__tests__/**/*.js",
"jest/**/*.js",
"packages/rn-tester/**/*.js",
],
"globals": {
// Expose some Jest globals for test helpers
"afterAll": true,
"afterEach": true,
"beforeAll": true,
"beforeEach": true,
"expect": true,
"jest": true,
},
},
{
"files": [
"**/__tests__/**/*-test.js",
],
"env": {
"jasmine": true,
"jest": true
}
}
]
}
-105
View File
@@ -1,105 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
'use strict';
const path = require('node:path');
require('eslint-plugin-lint').load(path.join(__dirname, 'tools/eslint/rules'));
module.exports = {
root: true,
extends: ['@react-native'],
plugins: ['@react-native/eslint-plugin-specs', 'lint'],
overrides: [
// overriding the JS config from eslint-config-react-native-community config to ensure
// that we use hermes-eslint for all js files
{
files: ['*.js'],
parser: 'hermes-eslint',
rules: {
// These rules are not required with hermes-eslint
'ft-flow/define-flow-type': 0,
'ft-flow/use-flow-type': 0,
// flow handles this check for us, so it's not required
'no-undef': 0,
},
},
{
files: ['packages/react-native/Libraries/**/*.js'],
rules: {
'@react-native/platform-colors': 2,
'@react-native/specs/react-native-modules': 2,
'lint/no-haste-imports': 2,
'lint/no-react-native-imports': 2,
'lint/require-extends-error': 2,
'lint/sort-imports': 1,
},
},
{
files: ['packages/react-native/flow-typed/**/*.js'],
rules: {
'lint/valid-flow-typed-signature': 2,
'no-unused-vars': 0,
quotes: 0,
},
},
{
files: [
'**/__fixtures__/**/*.js',
'**/__mocks__/**/*.js',
'**/__tests__/**/*.js',
'packages/react-native/jest/**/*.js',
'packages/rn-tester/**/*.js',
],
globals: {
// Expose some Jest globals for test helpers
afterAll: true,
afterEach: true,
beforeAll: true,
beforeEach: true,
expect: true,
jest: true,
},
},
{
files: ['**/__tests__/**/*-test.js'],
env: {
jasmine: true,
jest: true,
},
},
{
files: ['**/*.{ts,tsx}'],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint/eslint-plugin'],
rules: {
'@typescript-eslint/no-unused-vars': 'off',
'react-native/no-inline-styles': 'off',
'@typescript-eslint/no-shadow': 'off',
'no-self-compare': 'off',
'react/self-closing-comp': 'off',
},
},
{
files: ['**/*.d.ts'],
plugins: ['redundant-undefined'],
rules: {
'redundant-undefined/redundant-undefined': [
'error',
{followExactOptionalPropertyTypes: true},
],
},
},
],
};
+10 -12
View File
@@ -3,16 +3,16 @@
.*/*[.]android.js
; Ignore templates for 'react-native init'
<PROJECT_ROOT>/packages/react-native/template/.*
<PROJECT_ROOT>/template/.*
; Ignore the Dangerfile
<PROJECT_ROOT>/packages/react-native-bots/dangerfile.js
<PROJECT_ROOT>/bots/dangerfile.js
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
; Flow doesn't support platforms
.*/packages/react-native/Libraries/Utilities/LoadingView.js
.*/Libraries/Utilities/LoadingView.js
.*/node_modules/resolve/test/resolver/malformed_package_json/package\.json$
@@ -25,13 +25,10 @@
.*/node_modules/.*
[libs]
packages/react-native/interface.js
packages/react-native/flow/
packages/react-native/flow-typed/
interface.js
flow/
[options]
enums=true
emoji=true
exact_by_default=true
@@ -44,9 +41,9 @@ module.file_ext=.ios.js
munge_underscores=true
module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/packages/react-native/index.js'
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/packages/react-native\1'
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/packages/react-native/Libraries/Image/RelativeImageStub'
module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/index.js'
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/\1'
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/Libraries/Image/RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
@@ -63,6 +60,7 @@ nonstrict-import=warn
deprecated-type=error
unsafe-getters-setters=warn
unnecessary-invariant=warn
signature-verification-failure=warn
[strict]
deprecated-type
@@ -74,4 +72,4 @@ untyped-import
untyped-type-import
[version]
^0.202.0
^0.170.0
+10 -12
View File
@@ -3,16 +3,16 @@
.*/*[.]ios.js
; Ignore templates for 'react-native init'
<PROJECT_ROOT>/packages/react-native/template/.*
<PROJECT_ROOT>/template/.*
; Ignore the Dangerfile
<PROJECT_ROOT>/packages/react-native-bots/dangerfile.js
<PROJECT_ROOT>/bots/dangerfile.js
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
; Flow doesn't support platforms
.*/packages/react-native/Libraries/Utilities/LoadingView.js
.*/Libraries/Utilities/LoadingView.js
.*/node_modules/resolve/test/resolver/malformed_package_json/package\.json$
@@ -25,13 +25,10 @@
.*/node_modules/.*
[libs]
packages/react-native/interface.js
packages/react-native/flow/
packages/react-native/flow-typed/
interface.js
flow/
[options]
enums=true
emoji=true
exact_by_default=true
@@ -44,9 +41,9 @@ module.file_ext=.android.js
munge_underscores=true
module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/packages/react-native/index.js'
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/packages/react-native\1'
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/packages/react-native/Libraries/Image/RelativeImageStub'
module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/index.js'
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/\1'
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/Libraries/Image/RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
@@ -63,6 +60,7 @@ nonstrict-import=warn
deprecated-type=error
unsafe-getters-setters=warn
unnecessary-invariant=warn
signature-verification-failure=warn
[strict]
deprecated-type
@@ -74,4 +72,4 @@ untyped-import
untyped-type-import
[version]
^0.202.0
^0.170.0
+1 -1
View File
@@ -9,7 +9,7 @@
# GitHub Settings, Bots
/.github/ @hramos
/packages/react-native-bots @hramos
/bots @hramos
# Continuous Integration
/.circleci/ @hramos
+6 -7
View File
@@ -10,15 +10,15 @@ body:
id: description
attributes:
label: Description
description: Please provide a clear and concise description of what the bug is. Include screenshots if needed. Test using the [latest React Native release](https://github.com/facebook/react-native/releases/latest) to make sure your issue has not already been fixed.
description: Please provide a clear and concise description of what the bug is. Include screenshots if needed. Please test using the latest React Native release to make sure your issue has not already been fixed - https://reactnative.dev/docs/upgrading.html
validations:
required: true
- type: input
id: version
attributes:
label: React Native Version
description: What is the latest version of react-native that this issue reproduces on? Please only list the highest version you tested. Bear in mind that only issues on [supported versions](https://github.com/reactwg/react-native-releases#which-versions-are-currently-supported) will be looked into.
placeholder: ex. 0.71.0
label: Version
description: What react-native version does this appear on?
placeholder: ex. 0.66.0
validations:
required: true
- type: textarea
@@ -40,9 +40,8 @@ body:
attributes:
label: Snack, code example, screenshot, or link to a repository
description: |
Please provide a Snack (https://snack.expo.dev/), a link to a repository on GitHub, or provide a minimal code example that reproduces the problem.
Please provide a Snack (https://snack.expo.io/), a link to a repository on GitHub, or provide a minimal code example that reproduces the problem.
You may provide a screenshot of the application if you think it is relevant to your bug report.
Here are some tips for providing a minimal example: https://stackoverflow.com/help/mcve
Please note that a reproducer is **mandatory**. Issues without reproducer are more likely to stall and will be closed.
validations:
required: true
required: false
-3
View File
@@ -6,9 +6,6 @@ contact_links:
- name: 🤔 Questions and Help
url: https://reactnative.dev/help
about: Looking for help with your app? Please refer to the React Native community's support resources.
- name: 💫 New Architecture - Questions & Technical Deep dive insights
url: https://github.com/reactwg/react-native-new-architecture
about: Questions and doubts related to technical questions for the New Architecture should be directed to the Working Group. Instructions on how to join are available in the README.
- name: 🚀 Discussions and Proposals
url: https://github.com/react-native-community/discussions-and-proposals
about: Discuss the future of React Native in the React Native community's discussions and proposals repository.
@@ -1,53 +0,0 @@
name: 💫 New Architecture - Bug Report
description: Report a reproducible bug or a build issue when using the New Architecture (Fabric & TurboModules) in React Native.
labels: ["Needs: Triage :mag:", "Type: New Architecture"]
body:
- type: markdown
attributes:
value: |
Please provide all the information requested. Issues that do not follow this format are going to be closed.
This issue report is reserved to bug & build issues for users on the New Architecture. If you're not using
the New Architecture, please don't open issues on this category.
- type: textarea
id: description
attributes:
label: Description
description: |
Please provide a clear and concise description of what the bug or issue is. Include screenshots if needed.
Please make sure you check the New Architecture documentation first, as your issue might
already be answered there - https://reactnative.dev/docs/next/new-architecture-intro
validations:
required: true
- type: input
id: version
attributes:
label: React Native Version
description: What is the latest version of react-native that this issue reproduces on? Please test against the latest stable version, and list only the highest version you tested. Bug reports against older versions are more likely to stall.
placeholder: ex. 0.71.0
validations:
required: true
- type: textarea
id: react-native-info
attributes:
label: Output of `npx react-native info`
description: Run `npx react-native info` in your terminal, copy and paste the results here.
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Steps to reproduce
description: Provide a detailed list of steps that reproduce the issue.
validations:
required: true
- type: textarea
id: extra
attributes:
label: Snack, code example, screenshot, or link to a repository
description: |
Please provide a Snack (https://snack.expo.dev/), a link to a repository on GitHub, or provide a minimal code example that reproduces the problem.
You may provide a screenshot of the application if you think it is relevant to your bug report.
Here are some tips for providing a minimal example: https://stackoverflow.com/help/mcve
Please note that a reproducer is **mandatory**. Issues without reproducer are more likely to stall and will be closed.
validations:
required: true
@@ -1,11 +1,11 @@
name: ⬆️ Upgrade - Build Regression
name: Upgrade - Build Regression
description: If you are upgrading to a new React Native version (stable or pre-release) and encounter a build regression.
labels: ["Needs: Triage :mag:", "Type: Upgrade Issue"]
body:
- type: markdown
attributes:
value: |
Please use this form to file an issue if you have upgraded or are upgrading to [latest stable release](https://github.com/facebook/react-native/releases/latest) and have experienced a regression (something that used to work in previous version).
Please use this form to file an issue if you have upgarded or are upgrading to [latest stable release](https://github.com/facebook/react-native/releases/latest) and have experienced a regression (something that used to work in previous version).
- type: input
id: new-version
attributes:
+4 -8
View File
@@ -6,16 +6,12 @@
## Changelog
<!-- Help reviewers and the release process by writing your own changelog entry.
Pick one each for the category and type tags:
[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message
For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->
[CATEGORY] [TYPE] - Message
## Test Plan
<!-- Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes the user interface. -->
-23
View File
@@ -1,23 +0,0 @@
<!-- Template for pre-release GitHub release -->
<!-- TODO Post about this release to https://github.com/reactwg/react-native-releases/discussions) -->
- <!-- TODO List out notable picks for this patch -->
---
To test it, run:
npx react-native init RN__SHORT_VERSION__ --version __VERSION__
---
You can participate in the conversation on the status of this release in the [working group](https://github.com/reactwg/react-native-releases/discussions).
---
To help you upgrade to this version, you can use the [upgrade helper](https://react-native-community.github.io/upgrade-helper/) ⚛️
---
See changes from this release in the [changelog PR](https://github.com/facebook/react-native/labels/%F0%9F%93%9D%20Changelog)
@@ -0,0 +1,52 @@
# Configuration for Respond To Issue Based on Label https://github.com/marketplace/actions/respond-to-issue-based-on-label
"Type: Invalid":
close: true
"Type: Question":
comment: >
We are using GitHub issues exclusively to track bugs in React Native. GitHub may not be the ideal place to ask a question, but you can try asking over on [Stack Overflow](http://stackoverflow.com/questions/tagged/react-native), or on [Reactiflux](https://www.reactiflux.com/).
close: true
"Type: Docs":
comment: >
Please report documentation issues in the [`react-native-website`](https://github.com/facebook/react-native-website/issues) repository.
close: true
"Resolution: For Stack Overflow":
comment: >
We are using GitHub issues exclusively to track bugs in the core React Native library. Please try asking over on [Stack Overflow](http://stackoverflow.com/questions/tagged/react-native) as it is better suited for this type of question.
close: true
"Needs: Issue Template":
comment: >
<table><tbody><tr><th width="50">:warning:</th><th width="100%">
Missing Required Fields
</th></tr><tr><td>:information_source:</td><td>
It looks like your issue may be missing some necessary information. GitHub provides an example template whenever a <a href="https://github.com/facebook/react-native/issues/new?template=bug_report.md">new issue is created</a>. Could you go back and make sure to fill out the template? You may edit this issue, or close it and open a new one.
</td></tr></tbody></table>
labels:
- "Needs: Author Feedback"
"Needs: Environment Info":
comment: >
<table><tbody><tr><th width="50">:warning:</th><th width="100%">
Missing Environment Information
</th></tr><tr><td>:information_source:</td><td>
Your issue may be missing information about your development environment. You can obtain the missing information by running <code>react-native info</code> in a console.
</td></tr></tbody></table>
labels:
- "Needs: Author Feedback"
"Needs: Verify on Latest Version":
comment: >
<table><tbody><tr><th width="50">:warning:</th><th width="100%">
Using Old Version
</th></tr><tr><td>:information_source:</td><td>
It looks like you are using an older version of React Native. Please <a href="https://reactnative.dev/docs/upgrading">upgrade</a> to the latest version, and verify if the issue persists. If it does not, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the current release.
</td></tr></tbody></table>
labels:
- "Needs: Author Feedback"
"Needs: Repro":
comment: >
<table><tbody><tr><th width="50">:warning:</th><th width="100%">
Missing Reproducible Example
</th></tr><tr><td>:information_source:</td><td>
It looks like your issue is missing a reproducible example. Please provide a <a href="https://snack.expo.io">Snack</a> or a repository that demonstrates the issue you are reporting in a <a href="https://stackoverflow.com/help/minimal-reproducible-example">minimal, complete, and reproducible</a> manner.
</td></tr></tbody></table>
labels:
- "Needs: Author Feedback"
-137
View File
@@ -1,137 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
module.exports = async (github, context, label) => {
const closeIssue = async () => {
await github.rest.issues.update({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
state: 'closed',
});
};
const addComment = async comment => {
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment,
});
};
const requestAuthorFeedback = async () => {
// Remove the triage label if it exists (ignore the 404 if not; it's not expected to always be there)
try {
await github.rest.issues.removeLabel({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
name: 'Needs: Triage :mag:',
});
} catch {}
await github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['Needs: Author Feedback'],
});
};
switch (label) {
case 'Type: Invalid':
await addComment(
`| :warning: | Issue is Invalid |\n` +
`| --- | --- |\n` +
`| :information_source: | This issue doesn't match any of the expected types for this repository - closing. |`,
);
await closeIssue();
return;
case 'Type: Question':
await addComment(
`| :warning: | Issue is a Question |\n` +
`| --- | --- |\n` +
`| :information_source: | We are using GitHub issues exclusively to track bugs in React Native. GitHub may not be the ideal place to ask a question, but you can try asking over on [Stack Overflow](http://stackoverflow.com/questions/tagged/react-native), or on [Reactiflux](https://www.reactiflux.com/). |`,
);
await closeIssue();
return;
case 'Type: Docs':
await addComment(
`| :warning: | Documentation Issue |\n` +
`| --- | --- |\n` +
`| :information_source: | Please report documentation issues in the [react-native-website](https://github.com/facebook/react-native-website/issues) repository. |`,
);
await closeIssue();
return;
case 'Resolution: For Stack Overflow':
await addComment(
`| :warning: | Issue is a Question |\n` +
`| --- | --- |\n` +
`| :information_source: | We are using GitHub issues exclusively to track bugs in the core React Native library. Please try asking over on [Stack Overflow](http://stackoverflow.com/questions/tagged/react-native) as it is better suited for this type of question. |`,
);
await closeIssue();
return;
case 'Type: Expo':
await addComment(
`| :warning: | Issue is Related to Expo |\n` +
`| --- | --- |\n` +
`| :information_source: | It looks like your issue is related to Expo and not React Native core. Please open your issue in [Expo's repository](https://github.com/expo/expo/issues/new). If you are able to create a repro that showcases that this issue is also happening in React Native vanilla, we will be happy to re-open. |`,
);
await closeIssue();
return;
case 'Needs: Issue Template':
await addComment(
`| :warning: | Missing Required Fields |\n` +
`| --- | --- |\n` +
`| :information_source: | It looks like your issue may be missing some necessary information. GitHub provides an example template whenever a [new issue is created](https://github.com/facebook/react-native/issues/new?template=bug_report.md). Could you go back and make sure to fill out the template? You may edit this issue, or close it and open a new one. |`,
);
await requestAuthorFeedback();
return;
case 'Needs: Environment Info':
await addComment(
`| :warning: | Missing Environment Information |\n` +
`| --- | --- |\n` +
`| :information_source: | Your issue may be missing information about your development environment. You can obtain the missing information by running <code>react-native info</code> in a console. |`,
);
await requestAuthorFeedback();
return;
case 'Needs: Verify on Latest Version':
await addComment(
`| :warning: | Newer Version of React Native is Available! |\n` +
`| --- | --- |\n` +
`| :information_source: | You are on a supported minor version, but it looks like there's a newer patch available. Please [upgrade](https://reactnative.dev/docs/upgrading) to the highest patch for your minor or latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If it does not repro, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the most recent releases. |`,
);
return;
case 'Needs: Version Info':
await addComment(
`| :warning: | Add or Reformat Version Info |\n` +
`| --- | --- |\n` +
`| :information_source: | We could not find or parse the version number of React Native in your issue report. Please use the template, and report your version including major, minor, and patch numbers - e.g. 0.70.2 |`,
);
await requestAuthorFeedback();
return;
case 'Needs: Repro':
await addComment(
`| :warning: | Missing Reproducible Example |\n` +
`| --- | --- |\n` +
`| :information_source: | It looks like your issue is missing a reproducible example. Please provide a [Snack](https://snack.expo.dev) or a repository that demonstrates the issue you are reporting in a [minimal, complete, and reproducible](https://stackoverflow.com/help/minimal-reproducible-example) manner. |`,
);
await requestAuthorFeedback();
return;
case 'Type: Unsupported Version':
await addComment(
`| :warning: | Unsupported Version of React Native |\n` +
`| --- | --- |\n` +
`| :information_source: | It looks like your issue or the example you provided uses an [unsupported version of React Native](https://github.com/reactwg/react-native-releases/blob/main/README.md#releases-support-policy). Due to the number of issues we receive, we're currently only accepting new issues against one of the supported versions. Please [upgrade](https://reactnative.dev/docs/upgrading) to latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If you cannot upgrade, please open your issue on [StackOverflow](https://stackoverflow.com/questions/tagged/react-native) to get further community support. |`,
);
await requestAuthorFeedback();
return;
}
};
@@ -4,16 +4,10 @@ on:
issues:
types: [opened, edited]
permissions:
contents: read
jobs:
add-version-label-issue:
permissions:
issues: write # for react-native-community/actions-apply-version-label to label issues
runs-on: ubuntu-latest
continue-on-error: true
if: github.repository == 'facebook/react-native'
steps:
- uses: react-native-community/actions-apply-version-label@v0.0.3
-24
View File
@@ -1,24 +0,0 @@
name: Automatic Rebase
on:
issue_comment:
types: [created]
permissions:
contents: read
jobs:
rebase:
permissions:
contents: write # for cirrus-actions/rebase to push code to rebase
pull-requests: read # for cirrus-actions/rebase to get info about PR
name: Rebase
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase')
runs-on: ubuntu-latest
steps:
- name: Checkout the latest code
uses: actions/checkout@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0 # otherwise, you will fail to push refs to dest repo
- name: Automatic Rebase
uses: cirrus-actions/rebase@1.7
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-63
View File
@@ -1,63 +0,0 @@
name: Label closed PR as merged and leave a comment
on:
push
permissions:
contents: read
pull-requests: write
jobs:
comment-and-label:
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
steps:
- uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
if(!context.payload.commits || !context.payload.commits.length) return;
const sha = context.payload.commits[0].id;
const {commit, author} = (await github.rest.repos.getCommit({
ref: sha,
owner: context.repo.owner,
repo: context.repo.repo,
})).data;
// Looking at the commit message, checks which PR number, if any, was closed by this commit
const getClosedPrIfExists = (commit) => {
if(!commit || !commit.message) return;
const prClosingRegex = /Closes https:\/\/github.com\/facebook\/react-native\/pull\/([0-9]+)|Pull Request resolved: https:\/\/github.com\/facebook\/react-native\/pull\/([0-9]+)/;
const prClosingMatch = commit.message.match(prClosingRegex);
if(!prClosingMatch || (!prClosingMatch[1] && ! prClosingMatch[2])) return;
return prClosingMatch[1] ?? prClosingMatch[2];
};
const closedPrNumber = getClosedPrIfExists(commit);
if(!closedPrNumber) return;
const pr = (await github.rest.pulls.get({
pull_number: closedPrNumber,
owner: context.repo.owner,
repo: context.repo.repo,
})).data;
// If the PR has already been processed (labeled as Merged), skip it
const mergedLabel = "Merged";
if(pr.labels && pr.labels.some(label => label.name === mergedLabel)) return;
const authorName = author?.login ? `@${author.login}` : commit.author.name;
github.rest.issues.createComment({
issue_number: closedPrNumber,
owner: context.repo.owner,
repo: context.repo.repo,
body: `This pull request was successfully merged by ${authorName} in **${sha}**.\n\n<sup>[When will my fix make it into a release?](https://github.com/facebook/react-native/wiki/Release-FAQ#when-will-my-fix-make-it-into-a-release) | [Upcoming Releases](https://github.com/reactwg/react-native-releases/discussions/categories/releases)</sup>`
});
github.rest.issues.addLabels({
issue_number: closedPrNumber,
owner: context.repo.owner,
repo: context.repo.repo,
labels: [mergedLabel]
});
-28
View File
@@ -1,28 +0,0 @@
name: Run Danger on PR
on:
pull_request_target:
types: [opened, edited, reopened, synchronize]
permissions:
actions: write
checks: write
contents: write
issues: write
pull-requests: write
statuses: write
jobs:
danger:
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
steps:
- uses: actions/checkout@v3
- name: Run Yarn Install on Root
run: yarn install
working-directory: .
- name: Danger
run: yarn danger ci --use-github-checks --failOnErrors
working-directory: packages/react-native-bots
env:
DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1 -8
View File
@@ -4,19 +4,12 @@ on:
issue_comment:
types: created
permissions:
contents: read
jobs:
applyNeedsAttentionLabel:
permissions:
contents: read # for actions/checkout to fetch code
issues: write # for hramos/needs-attention to label issues
name: Apply Needs Attention Label
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Apply Needs Attention Label
uses: hramos/needs-attention@v1
with:
+6 -36
View File
@@ -4,43 +4,13 @@ on:
issues:
types: labeled
permissions:
contents: write
issues: write
jobs:
# Runs automatic checks on issues labeled with "Needs: Triage",
# then invokes actOnLabel to react to any added labels
triage-issue:
respondToIssueBasedOnLabel:
name: Respond to Issue Based on Label
runs-on: ubuntu-latest
if: "${{ github.repository == 'facebook/react-native' && contains(github.event.label.name, 'Needs: Triage :mag:') }}"
steps:
- uses: actions/checkout@v3
- uses: actions/github-script@v6
- uses: actions/checkout@v2
- name: Respond to Issue Based on Label
uses: hramos/respond-to-issue-based-on-label@v2
with:
script: |
const verifyVersion = require('./.github/workflows/verifyVersion.js')
const labelToAdd = await verifyVersion(github, context);
if(labelToAdd) {
await github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: [labelToAdd]
})
const actOnLabel = require('./.github/workflows/actOnLabel.js')
await actOnLabel(github, context, labelToAdd)
}
# Reacts to the label that triggered this workflow (added manually or via other workflows)
act-on-label:
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
steps:
- uses: actions/checkout@v3
- uses: actions/github-script@v6
with:
script: |
const actOnLabel = require('./.github/workflows/actOnLabel.js')
await actOnLabel(github, context, context.payload.label.name)
repo-token: ${{ secrets.GITHUB_TOKEN }}
+5 -63
View File
@@ -1,77 +1,19 @@
name: Mark stale issues and pull requests
on:
schedule:
- cron: "0 0,6,12,18 * * *"
- cron: "30 1 * * *"
jobs:
stale:
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
- uses: actions/stale@v4
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 180
stale-issue-message: 'This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
stale-pr-message: 'This PR is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
days-before-stale: 365
stale-issue-message: 'This issue is stale because it has been open 365 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
stale-pr-message: 'This PR is stale because it has been open 365 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
close-issue-message: 'This issue was closed because it has been stalled for 7 days with no activity.'
close-pr-message: 'This PR was closed because it has been stalled for 7 days with no activity.'
exempt-issue-labels: Help Wanted :octocat:, Good first issue, Never gets stale
exempt-pr-labels: Help Wanted :octocat:, Good first issue, Never gets stale
stale-asc:
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
with:
ascending: true
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 180
stale-issue-message: 'This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
stale-pr-message: 'This PR is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
close-issue-message: 'This issue was closed because it has been stalled for 7 days with no activity.'
close-pr-message: 'This PR was closed because it has been stalled for 7 days with no activity.'
exempt-issue-labels: Help Wanted :octocat:, Good first issue, Never gets stale
exempt-pr-labels: Help Wanted :octocat:, Good first issue, Never gets stale
stale-needs-author-feedback:
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
any-of-labels: 'Needs: Author Feedback'
days-before-stale: 24
stale-issue-message: "This issue is waiting for author's feedback since 24 days. Please provide the requested feedback or this will be closed in 7 days."
stale-pr-message: "This PR is waiting for author's feedback since 24 days. Please provide the requested feedback or this will be closed in 7 days"
close-issue-message: "This issue was closed because the author hasn't provided the requested feedback after 7 days."
close-pr-message: "This PR was closed because the author hasn't provided the requested feedback after 7 days."
exempt-issue-labels: Help Wanted :octocat:, Good first issue, Never gets stale
exempt-pr-labels: Help Wanted :octocat:, Good first issue, Never gets stale
stale-needs-author-feedback-asc:
runs-on: ubuntu-latest
if: github.repository == 'facebook/react-native'
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
with:
ascending: true
repo-token: ${{ secrets.GITHUB_TOKEN }}
any-of-labels: 'Needs: Author Feedback'
days-before-stale: 24
stale-issue-message: "This issue is waiting for author's feedback since 24 days. Please provide the requested feedback or this will be closed in 7 days."
stale-pr-message: "This PR is waiting for author's feedback since 24 days. Please provide the requested feedback or this will be closed in 7 days"
close-issue-message: "This issue was closed because the author hasn't provided the requested feedback after 7 days."
close-pr-message: "This PR was closed because the author hasn't provided the requested feedback after 7 days."
exempt-issue-labels: Help Wanted :octocat:, Good first issue, Never gets stale
exempt-pr-labels: Help Wanted :octocat:, Good first issue, Never gets stale
+19
View File
@@ -0,0 +1,19 @@
name: Test Docker Android Image
# This workflow is triggered on commits to main and pull requests.
on:
push:
branches:
- main
pull_request:
types: [ synchronize ]
branches:
- main
jobs:
test-docker-android:
name: Test Docker
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build Docker image with Android test app
run: npm run docker-build-android
-117
View File
@@ -1,117 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
module.exports = async (github, context) => {
const issue = context.payload.issue;
// Ignore issues using upgrade template (they use a special label)
if (issue.labels.find(label => label.name == 'Type: Upgrade Issue')) {
return;
}
const issueVersionUnparsed =
getReactNativeVersionFromIssueBodyIfExists(issue);
const issueVersion = parseVersionFromString(issueVersionUnparsed);
// Nightly versions are always supported
if (reportedVersionIsNightly(issueVersionUnparsed, issueVersion)) return;
if (!issueVersion) {
return 'Needs: Version Info';
}
// Ensure the version matches one we support
const recentReleases = (
await github.rest.repos.listReleases({
owner: context.repo.owner,
repo: context.repo.repo,
})
).data.map(release => release.name);
const latestRelease = (
await github.rest.repos.getLatestRelease({
owner: context.repo.owner,
repo: context.repo.repo,
})
).data;
const latestVersion = parseVersionFromString(latestRelease.name);
if (!isVersionSupported(issueVersion, latestVersion)) {
return 'Type: Unsupported Version';
}
// We want to encourage users to repro the issue on the highest available patch for the given minor.
const latestPatchForVersion = getLatestPatchForVersion(
issueVersion,
recentReleases,
);
if (latestPatchForVersion > issueVersion.patch) {
return 'Needs: Verify on Latest Version';
}
};
// We support N-2 minor versions, and the latest major.
function isVersionSupported(actualVersion, latestVersion) {
return (
actualVersion.major >= latestVersion.major &&
actualVersion.minor >= latestVersion.minor - 2
);
}
// Assumes that releases are sorted in the order of recency (i.e. most recent releases are earlier in the list)
// This enables us to stop looking as soon as we find the first release with a matching major/minor version, since
// we know it's the most recent release, therefore the highest patch available.
function getLatestPatchForVersion(version, releases) {
for (releaseName of releases) {
const release = parseVersionFromString(releaseName);
if (
release &&
release.major == version.major &&
release.minor == version.minor
) {
return release.patch;
}
}
}
function getReactNativeVersionFromIssueBodyIfExists(issue) {
if (!issue || !issue.body) return;
const rnVersionRegex = /React Native Version[\r\n]+(?<version>.+)[\r\n]*/;
const rnVersionMatch = issue.body.match(rnVersionRegex);
if (!rnVersionMatch || !rnVersionMatch.groups.version) return;
return rnVersionMatch.groups.version;
}
function reportedVersionIsNightly(unparsedVersionString, version) {
if (!unparsedVersionString && !version) return false;
const nightlyRegex = /nightly/i;
const nightlyMatch = unparsedVersionString.match(nightlyRegex);
const versionIsNightly = nightlyMatch && nightlyMatch[0];
const versionIsZero =
version && version.major == 0 && version.minor == 0 && version.patch == 0;
return versionIsZero || versionIsNightly;
}
function parseVersionFromString(version) {
if (!version) return;
// This will match the standard x.x.x semver format, as well as the non-standard prerelease x.x.x-rc.x
const semverRegex =
/(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(-[rR]{1}[cC]{1}\.(?<prerelease>\d+))?/;
const versionMatch = version.match(semverRegex);
if (!versionMatch) return;
const {major, minor, patch, prerelease} = versionMatch.groups;
return {
major: parseInt(major),
minor: parseInt(minor),
patch: parseInt(patch),
prerelease: parseInt(prerelease),
};
}
+28 -54
View File
@@ -20,36 +20,30 @@ DerivedData
*.ipa
*.xcuserstate
project.xcworkspace
**/.xcode.env.local
# Gradle
/build/
/packages/react-native-gradle-plugin/build/
/packages/rn-tester/build
/packages/rn-tester/android/app/.cxx/
/packages/rn-tester/android/app/build/
/packages/rn-tester/android/app/gradle/
/packages/rn-tester/android/app/gradlew
/packages/rn-tester/android/app/gradlew.bat
/packages/react-native/ReactAndroid/build/
/packages/react-native/ReactAndroid/.cxx/
/packages/react-native/ReactAndroid/gradle/
/packages/react-native/ReactAndroid/gradlew
/packages/react-native/ReactAndroid/gradlew.bat
/packages/react-native/ReactAndroid/external-artifacts/build/
/packages/react-native/ReactAndroid/external-artifacts/artifacts/
/packages/react-native/ReactAndroid/hermes-engine/build/
/packages/react-native/ReactAndroid/hermes-engine/.cxx/
/packages/react-native/template/android/app/build/
/packages/react-native/template/android/build/
/ReactAndroid/build/
/ReactAndroid/.cxx/
/ReactAndroid/gradle/
/ReactAndroid/gradlew
/ReactAndroid/gradlew.bat
/template/android/app/build/
/template/android/build/
# Buck
.buckd
buck-out
/.lsp.buckd
/.lsp-buck-out
/packages/react-native/ReactAndroid/src/main/jni/prebuilt/lib/
/packages/react-native/ReactAndroid/src/main/gen
/ReactAndroid/src/main/jni/prebuilt/lib/
/ReactAndroid/src/main/gen
# Android Studio
.project
@@ -64,76 +58,59 @@ buck-out
.gradle
local.properties
*.iml
/packages/react-native/android/*
!/packages/react-native/android/README.md
/android/
# Node
node_modules
*.log
.nvm
/bots/node_modules/
package-lock.json
# OS X
.DS_Store
# Test generated files
/packages/react-native/ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
/ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
*.js.meta
/coverage
/third-party
# Test Reports
/reports
# Stack Dumps generated when programs crash (Ex. bash.exe.stackdump on Win)
*.stackdump
# Root dir shouldn't have Xcode project
/*.xcodeproj
# ReactCommon subdir shouldn't have Xcode project
/packages/react-native/ReactCommon/**/*.xcodeproj
/ReactCommon/**/*.xcodeproj
/packages/rn-tester/build
/packages/rn-tester/android/app/build/*
# Libs that shouldn't have Xcode project
/packages/react-native/Libraries/FBLazyVector/**/*.xcodeproj
/packages/react-native/Libraries/RCTRequired/**/*.xcodeproj
/packages/react-native/React/CoreModules/**/*.xcodeproj
/packages/react-native/React/FBReactNativeSpec/**/*.xcodeproj
/Libraries/FBLazyVector/**/*.xcodeproj
/Libraries/RCTRequired/**/*.xcodeproj
/React/CoreModules/**/*.xcodeproj
/React/FBReactNativeSpec/**/*.xcodeproj
/packages/react-native-codegen/**/*.xcodeproj
# Ruby Gems (Bundler)
/packages/react-native/vendor
/packages/react-native/template/vendor
.ruby-version
/**/.ruby-version
./vendor/
vendor/
/vendor
/template/vendor
# iOS / CocoaPods
/packages/react-native/template/ios/build/
/packages/react-native/template/ios/Pods/
/packages/react-native/template/ios/Podfile.lock
/template/ios/build/
/template/ios/Pods/
/template/ios/Podfile.lock
/packages/rn-tester/Gemfile.lock
# Ignore RNTester specific Pods, but keep the __offline_mirrors__ here.
/packages/rn-tester/Pods/*
!/packages/rn-tester/Pods/__offline_mirrors_hermes__
!/packages/rn-tester/Pods/__offline_mirrors_jsc__
!/packages/rn-tester/Pods/__offline_mirrors__
# @react-native/codegen
/packages/react-native/React/FBReactNativeSpec/FBReactNativeSpec
# react-native-codegen
/React/FBReactNativeSpec/FBReactNativeSpec
/packages/react-native-codegen/lib
/packages/react-native-codegen/tmp/
/packages/react-native/ReactCommon/react/renderer/components/rncore/
/ReactCommon/react/renderer/components/rncore/
/packages/rn-tester/NativeModuleExample/ScreenshotManagerSpec*
/**/RCTThirdPartyFabricComponentsProvider.*
# Additional SDKs
/packages/react-native/sdks/download
/packages/react-native/sdks/hermes
/packages/react-native/sdks/hermesc
# Visual studio
.vscode
@@ -141,6 +118,3 @@ vendor/
# Android memory profiler files
*.hprof
# Temporary files created by Metro to check the health of the file watcher
.metro-health-check*
+1
View File
@@ -0,0 +1 @@
2.7.4
+231 -329
View File
File diff suppressed because it is too large Load Diff
-1521
View File
File diff suppressed because it is too large Load Diff
+110 -4
View File
@@ -1,7 +1,113 @@
# Contributing to React Native
Thank you for your interest in contributing to React Native! From commenting on and triaging issues, to reviewing and sending Pull Requests, all contributions are welcome.
We aim to build a vibrant and inclusive [ecosystem of partners, core contributors, and community](ECOSYSTEM.md) that goes beyond the main React Native GitHub repository.
Thank you for your interest in contributing to React Native! From commenting on and triaging issues, to reviewing and sending Pull Requests, all contributions are welcome. We aim to build a vibrant and inclusive [ecosystem of partners, core contributors, and community](ECOSYSTEM.md) that goes beyond the main React Native GitHub repository.
To learn more about how to contribute check out the Contributing section on the React Native website:
* https://reactnative.dev/contributing/overview
The [Open Source Guides](https://opensource.guide/) website has a collection of resources for individuals, communities, and companies who want to learn how to run and contribute to an open source project. Contributors and people new to open source alike will find the following guides especially useful:
* [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
* [Building Welcoming Communities](https://opensource.guide/building-community/)
### [Code of Conduct](https://github.com/facebook/react-native/blob/HEAD/CODE_OF_CONDUCT.md)
As a reminder, all contributors are expected to adhere to the [Code of Conduct](https://github.com/facebook/react-native/blob/HEAD/CODE_OF_CONDUCT.md).
## Ways to Contribute
If you are eager to start contributing code right away, we have a list of [good first issues](https://github.com/facebook/react-native/labels/good%20first%20issue) that contain bugs which have a relatively limited scope. As you gain more experience and demonstrate a commitment to evolving React Native, you may be granted issue management permissions in the repository.
There are other ways you can contribute without writing a single line of code. Here are a few things you can do to help out:
1. **Replying and handling open issues.** We get a lot of issues every day, and some of them may lack necessary information. You can help out by guiding people through the process of filling out the issue template, asking for clarifying information, or pointing them to existing issues that match their description of the problem. We cover more about this process in the [Issue Triage wiki](https://github.com/facebook/react-native/wiki/Triaging-GitHub-Issues).
2. **Reviewing pull requests for the docs.** Reviewing [documentation updates](https://github.com/facebook/react-native-website/pulls) can be as simple as checking for spelling and grammar. If you encounter situations that can be explained better in the docs, click **Edit** at the top of most docs pages to get started with your own contribution.
3. **Help people write test plans.** Some pull requests sent to the main repository may lack a proper test plan. These help reviewers understand how the change was tested, and can speed up the time it takes for a contribution to be accepted.
Each of these tasks is highly impactful, and maintainers will greatly appreciate your help.
### Our Development Process
We use GitHub issues and pull requests to keep track of bug reports and contributions from the community. All changes from engineers at Facebook will sync to [GitHub](https://github.com/facebook/react-native) through a bridge with Facebook's internal source control. Changes from the community are handled through GitHub pull requests. Once a change made on GitHub is approved, it will first be imported into Facebook's internal source control and tested against Facebook's codebase. Once merged at Facebook, the change will eventually sync back to GitHub as a single commit once it has passed Facebook's internal tests.
You can learn more about the contribution process in the following documents:
* [Issues](https://github.com/facebook/react-native/wiki/Triaging-GitHub-Issues)
* [Pull Requests](https://github.com/facebook/react-native/wiki/Managing-Pull-Requests)
We also have a thriving community of contributors who would be happy to help you get set up. You can reach out to us through [@ReactNative](http://twitter.com/reactnative) (the React Native team) and [@ReactNativeComm](http://twitter.com/reactnativecomm) (the React Native Community organization).
### Repositories
The main repository, <https://github.com/facebook/react-native>, contains the React Native framework itself and it is here where we keep track of bug reports and manage pull requests.
There are a few other repositories you might want to familiarize yourself with:
* **React Native website** which contains the source code for the website, including the documentation, located at <https://github.com/facebook/react-native-website>
* **Releases** Conversations for new releases are happening [in this discussion repo](https://github.com/reactwg/react-native-releases/discussions).
* **Changelog** The changelog can be found [here](https://github.com/facebook/react-native/blob/main/CHANGELOG.md).
* **Discussions** about the future of React Native take place in the <https://github.com/react-native-community/discussions-and-proposals> repository.
* **High-quality plugins** for React Native can be found throughout the [React Native Community GitHub Organization](http://github.com/react-native-community/).
Browsing through these repositories should provide some insight into how the React Native open source project is managed.
## GitHub Issues
We use GitHub issues to track bugs exclusively. We have documented our issue handling processes in the [Issues wiki](https://github.com/facebook/react-native/wiki/Triaging-GitHub-Issues).
### Security Bugs
Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe disclosure of security bugs. In those cases, please go through the process outlined on that page and do not file a public issue.
## Helping with Documentation
The React Native documentation is hosted as part of the React Native website repository at https://github.com/facebook/react-native-website. The website itself is located at <https://reactnative.dev/> and it is built using [Docusaurus](https://docusaurus.io/). If there's anything you'd like to change in the docs, you can get started by clicking on the "Edit" button located on the upper right of most pages in the website.
If you are adding new functionality or introducing a change in behavior, we will ask you to update the documentation to reflect your changes.
### Contributing to the Blog
The React Native blog is generated [from the Markdown sources for the blog](https://github.com/facebook/react-native-website/tree/HEAD/website/blog).
Please open an issue in the https://github.com/facebook/react-native-website repository or tag us on [@ReactNative on Twitter](http://twitter.com/reactnative) and get the go-ahead from a maintainer before writing an article intended for the React Native blog. In most cases, you might want to share your article on your own blog or writing medium instead. It's worth asking, though, in case we find your article is a good fit for the blog.
We recommend referring to the [CONTRIBUTING](https://github.com/facebook/react-native-website/blob/HEAD/CONTRIBUTING.md) document for the `react-native-website` repository to learn more about contributing to the website in general.
## Contributing Code
Code-level contributions to React Native generally come in the form of [pull requests](https://help.github.com/en/articles/about-pull-requests). These are done by forking the repo and making changes locally. Directly in the repo, there is the [`rn-tester` app](/packages/rn-tester) that you can install on your device (or simulators) and use to test the changes you're making to React Native sources.
The process of proposing a change to React Native can be summarized as follows:
1. Fork the React Native repository and create your branch from `main`.
2. Make the desired changes to React Native sources. Use the `packages/rn-tester` app to test them out.
3. If you've added code that should be tested, add tests.
4. If you've changed APIs, update the documentation, which lives in [another repo](https://github.com/facebook/react-native-website/).
5. Ensure the test suite passes, either locally or on CI once you opened a pull request.
6. Make sure your code lints (for example via `yarn lint --fix`).
7. Push the changes to your fork.
8. Create a pull request to the React Native repository.
9. Review and address comments on your pull request.
1. A bot may comment with suggestions. Generally we ask you to resolve these first before a maintainer will review your code.
2. If changes are requested and addressed, please [request review](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/requesting-a-pull-request-review) to notify reviewers to take another look.
10. If you haven't already, please complete the [Contributor License Agreement](https://github.com/facebook/react-native/wiki/Contributor-License-Agreement) ("CLA"). **[Complete your CLA here.](https://code.facebook.com/cla)**
If all goes well, your pull request will be merged. If it is not merged, maintainers will do their best to explain the reason why.
### Step-by-step Guide
Whenever you are ready to contribute code, check out our [step-by-step guide to sending your first pull request](https://github.com/facebook/react-native/wiki/How-to-Open-a-Pull-Request), or read the [How to Contribute Code](https://github.com/facebook/react-native/wiki/How-to-Contribute-Code) wiki for more details.
### Tests
Tests help us prevent regressions from being introduced to the codebase. The GitHub repository is continuously tested using Circle and Appveyor, the results of which are available through the Checks functionality on [commits](https://github.com/facebook/react-native/commits/HEAD) and pull requests. You can learn more about running and writing tests in the [Tests wiki](http://github.com/facebook/react-native/wiki/Tests).
## Community Contributions
Contributions to React Native are not limited to GitHub. You can help others by sharing your experience using React Native, whether that is through blog posts, presenting talks at conferences, or simply sharing your thoughts on Twitter and tagging @ReactNative.
## Where to Get Help
As you work on React Native, it is natural that sooner or later you may require help. In addition to the resources listed in [SUPPORT](.github/SUPPORT.md), people interested in contributing may take advantage of the following:
* **Twitter**. The React Native team at Facebook has its own account at [@reactnative](https://twitter.com/reactnative), and the React Native Community uses [@reactnativecomm](https://twitter.com/reactnativecomm). If you feel stuck, or need help contributing, please do not hesitate to reach out.
* **Proposals Repository**. If you are considering working on a feature large in scope, consider [creating a proposal first](https://github.com/react-native-community/discussions-and-proposals). The community can help you figure out the right approach, and we'd be happy to help.
* **React Native Community Discord**. While we try to hold most discussions in public, sometimes it can be beneficial to have conversations in real time with other contributors. People who have demonstrated a commitment to moving React Native forward through sustained contributions to the project may eventually be invited to join the React Native Community Discord.
+14 -29
View File
@@ -1,36 +1,27 @@
# The React Native Ecosystem
We aim to build a vibrant and inclusive ecosystem of partners, core contributors, and community that goes beyond the main React Native GitHub repository. This document explains the roles and responsibilities of various stakeholders and provides guidelines for the community organization. The structure outlined in this document has been in place for a while but had not been written down before.
We aim to build a vibrant and inclusive ecosystem of partners, core contributors, and community that goes beyond the main React Native GitHub repository. This document explains the roles and responsibilities of various stakeholders and provides guidelines for the community organization. The structure outlined in this document has been in place for a while but hadn't been written down before.
There are three types of stakeholders:
* **Partners:** Companies significantly invested in React Native and take responsibility for the React Native vision and community.
* **Partners:** Companies that are significantly invested in React Native and have been for years.
* **Core Contributors:** Individual people who contribute to the React Native project.
* **Community Contributors:** Individuals who support projects in the [react-native-community](https://github.com/react-native-community) organization.
## Partners
Partners are companies that are significantly invested in React Native and demonstrate ownership. Informed by their use of React Native, they push for improvements of the core and/or the ecosystem around it. Examples of this may include large scale contributions to `react-native` or owning essential tools or libraries.
Partners are companies that are significantly invested in React Native and have been for years. Informed by their use of React Native, they push for improvements of the core and/or the ecosystem around it. Partners think of React Native as a product: they understand the trade offs that the project makes as well as future plans and goals. Together we shape the vision for React Native to make it the best way to build applications.
Partners think of React Native as a product; they understand the trade offs that the project makes as well as future plans and goals. Together we shape the vision for React Native to make it the best way to build applications.
React Native's current set of partners include Callstack, Expo, Facebook, Infinite Red, Microsoft and Software Mansion. Many engineers from these companies are core contributors, and their partner responsibilities also include:
Baseline responsibilities of partners include:
* Attending monthly meeting
* Contributing to the release process. Examples include being a [community releaser](https://reactnative.dev/contributing/release-roles-responsibilities#release-role-2-community-releaser), testing new releases, technical support for release issues.
* Engage on the core contributor Discord
* **[Callstack](https://callstack.com/):** Manages releases, maintains the [React Native CLI](https://github.com/react-native-community/react-native-cli) and organizes [React Native EU](https://react-native.eu/)
* **[Expo](https://expo.io/):** Builds [expo](https://github.com/expo/expo) on top of React Native to simplify app development
* **[Facebook](https://opensource.facebook.com):** Oversees the React Native product and maintains the [React Native core repo](https://reactnative.dev/)
* **[Infinite Red](https://infinite.red/):** Maintains the [ignite cli/boilerplate](https://github.com/infinitered/ignite), organizes [Chain React Conf](https://cr.infinite.red/)
* **[Microsoft](http://aka.ms/reactnative):** Develops [React Native Windows](https://github.com/Microsoft/react-native-windows) and [React Native macOS](https://github.com/microsoft/react-native-macos) for building apps that target Windows and macOS
* **[Software Mansion](https://swmansion.com/):** Maintain core infrastructure including JSC, Animated, and other popular third-party plugins.
Our current partners and other areas of ownership:
* **[Coinbase](https://www.coinbase.com/):** Publishes [posts](https://blog.coinbase.com/tagged/react-native) advocating React Native usage. Supports `@react-native-community/datetimepicker` and other community modules to migrate to the new architecture. Supports releases in testing and feedback.
* **[Callstack](https://callstack.com/):** Maintains the [React Native CLI](https://github.com/react-native-community/react-native-cli) and [other community libraries](https://github.com/callstack), organizes [React Native EU](https://react-native.eu/) and hosts [The React Native Show podcast](https://www.callstack.com/podcast-react-native-show)
* **[Expo](https://expo.dev/):** Builds [Expo Go and SDK](https://github.com/expo/expo), [Snack](https://snack.expo.dev/), and [Expo Application Services](https://expo.dev/eas). Maintains [React Native Directory](https://reactnative.directory/), stewards [React Navigation](https://reactnavigation.org/) along with other partners.
* **[Infinite Red](https://infinite.red/):** Maintains the [ignite cli/boilerplate](https://github.com/infinitered/ignite), organizes [Chain React Conf](https://cr.infinite.red/), hosts the [React Native Radio podcast](https://reactnativeradio.com), publishes the [React Native Newsletter](https://reactnativenewsletter.com)
* **[Meta](https://opensource.fb.com/):** Oversees the React Native product and maintains the [React Native core repo](https://reactnative.dev/)
* **[Microsoft](https://twitter.com/ReactNativeMSFT):** Develops [React Native Windows](https://github.com/Microsoft/react-native-windows) and [React Native macOS](https://github.com/microsoft/react-native-macos) for building apps that target Windows and macOS; maintains [rnx-kit](https://github.com/microsoft/rnx-kit), [react-native-test-app](https://github.com/microsoft/react-native-test-app) and coordinates cross-companies efforts such as the [bundle working group](https://github.com/microsoft/rnx-kit/discussions/categories/bundle-working-group).
* **[Shopify](https://www.shopify.com/):** Maintains React Native open source libraries such as [flash-list](https://github.com/Shopify/flash-list) or [@shopify/react-native-skia](https://github.com/Shopify/react-native-skia) and sponsors Software Mansion.
* **[Software Mansion](https://swmansion.com/):** Maintain core infrastructure including JSC, Animated, and other popular third-party plugins and organizes [App.js Conf](https://appjs.co/)
* **[Wix.com](https://wix.engineering/open-source):** Maintains a variety of React Native open source projects ([see all](https://github.com/orgs/wix/repositories?q=react-native)), including: [Detox](https://wix.github.io/Detox/) end-to-end testing library for React Native apps, [RN UILib](https://wix.github.io/react-native-ui-lib/), [RN Navigation](https://wix.github.io/react-native-navigation/), [RN Calendars](https://wix.github.io/react-native-calendars/) and [RN Notifications](https://github.com/wix/react-native-notifications).
This list may fluctuate in response to newcomers who meet our partner definition. In terms of open source work, pull requests from partners are commonly prioritized. When you are contributing to React Native, you'll most likely meet somebody who works at one of the partner companies and who is a core contributor:
In terms of open source work, pull requests from partners are commonly prioritized. When you are contributing to React Native, you'll most likely meet somebody who works at one of the partner companies and who is a core contributor:
## Core Contributors
@@ -47,19 +38,13 @@ Core contributors are individuals who contribute to the React Native project. A
These are behaviors we have observed in our existing core contributors. They aren't strict rules but rather outline their usual responsibilities. We do not expect every core contributor to do all of the above things all the time. Most importantly, we want to create a supportive and friendly environment that fosters collaboration. Above all else, **we are always polite and friendly.**
Core contributor status is attained after consistently contributing and taking on the responsibilities outlined above and granted by other core contributors. Similarly, after a long period of inactivity (~6 months or more), a core contributor may be contacted to understand if theyre still interested in being part of the program.
Core contributor status is attained after consistently contributing and taking on the responsibilities outlined above and granted by other core contributors. Similarly, after a long period of inactivity, a core contributor may be removed.
You can use this [form](https://forms.gle/4jpA4QeNUvAUDnNe8) to either:
* Apply yourself to become a Core Contributor. Make sure to include a list of valuable contributions you did to the React Native repository and ecosystem.
* Nominate someone to become a Core Contributor.
As a core contributor, you will have access to the core contributor Discord which is used for light-weight coordination and discussion.
**We aim to make contributing to React Native as easy and transparent as possible.** We have discussion groups dedicated to the [new architecture rollout](https://github.com/reactwg/react-native-new-architecture), [releases](https://github.com/reactwg/react-native-releases), and [general questions and proposals](https://github.com/react-native-community/discussions-and-proposals). We are always looking for active, enthusiastic members of the React Native community to become core contributors.
We aim to make contributing to React Native as easy and transparent as possible. All important topics are handled through a [discussion or RFC process on GitHub](https://github.com/react-native-community/discussions-and-proposals). We are always looking for active, enthusiastic members of the React Native community to become core contributors.
## Community Contributors
Community contributors are individuals who support projects in the [react-native-community](https://github.com/react-native-community) organization. This organization exists as an incubator for high quality components that extend the capabilities of React Native with functionality that many but not all applications require. Meta engineers will provide guidance to help build a vibrant community of people and components that make React Native better.
Community contributors are individuals who support projects in the [react-native-community](https://github.com/react-native-community) organization. This organization exists as an incubator for high quality components that extend the capabilities of React Native with functionality that many but not all applications require. Facebook engineers will provide guidance to help build a vibrant community of people and components that make React Native better.
This structure has multiple benefits:
+2 -3
View File
@@ -1,7 +1,6 @@
source 'https://rubygems.org'
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
ruby ">= 2.6.10"
ruby '2.7.4'
gem 'cocoapods', '~> 1.12'
gem 'activesupport', '>= 6.1.7.3', '< 7.1.0'
gem 'cocoapods', '~> 1.11', '>= 1.11.2'
+24 -23
View File
@@ -3,27 +3,28 @@ GEM
specs:
CFPropertyList (3.0.6)
rexml
activesupport (7.0.8)
activesupport (6.1.7.2)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
addressable (2.8.6)
zeitwerk (~> 2.3)
addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0)
algoliasearch (1.27.5)
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
atomos (0.1.3)
claide (1.1.0)
cocoapods (1.14.3)
cocoapods (1.11.3)
addressable (~> 2.8)
claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.14.3)
cocoapods-core (= 1.11.3)
cocoapods-deintegrate (>= 1.0.3, < 2.0)
cocoapods-downloader (>= 2.1, < 3.0)
cocoapods-downloader (>= 1.4.0, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
cocoapods-search (>= 1.0.0, < 2.0)
cocoapods-trunk (>= 1.6.0, < 2.0)
cocoapods-trunk (>= 1.4.0, < 2.0)
cocoapods-try (>= 1.1.0, < 2.0)
colored2 (~> 3.1)
escape (~> 0.0.4)
@@ -31,10 +32,10 @@ GEM
gh_inspector (~> 1.0)
molinillo (~> 0.8.0)
nap (~> 1.0)
ruby-macho (>= 2.3.0, < 3.0)
xcodeproj (>= 1.23.0, < 2.0)
cocoapods-core (1.14.3)
activesupport (>= 5.0, < 8)
ruby-macho (>= 1.0, < 3.0)
xcodeproj (>= 1.21.0, < 2.0)
cocoapods-core (1.11.3)
activesupport (>= 5.0, < 7)
addressable (~> 2.8)
algoliasearch (~> 1.0)
concurrent-ruby (~> 1.1)
@@ -44,7 +45,7 @@ GEM
public_suffix (~> 4.0)
typhoeus (~> 1.0)
cocoapods-deintegrate (1.0.5)
cocoapods-downloader (2.1)
cocoapods-downloader (1.6.3)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.1)
@@ -53,47 +54,47 @@ GEM
netrc (~> 0.11)
cocoapods-try (1.2.0)
colored2 (3.1.2)
concurrent-ruby (1.2.3)
concurrent-ruby (1.2.0)
escape (0.0.4)
ethon (0.16.0)
ffi (>= 1.15.0)
ffi (1.16.3)
ffi (1.15.5)
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
httpclient (2.8.3)
i18n (1.14.1)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
json (2.7.1)
minitest (5.21.2)
json (2.6.3)
minitest (5.17.0)
molinillo (0.8.0)
nanaimo (0.3.0)
nap (1.1.0)
netrc (0.11.0)
public_suffix (4.0.7)
rexml (3.2.6)
rexml (3.2.5)
ruby-macho (2.5.1)
typhoeus (1.4.1)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
xcodeproj (1.23.0)
xcodeproj (1.22.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.3.0)
rexml (~> 3.2.4)
zeitwerk (2.6.6)
PLATFORMS
ruby
DEPENDENCIES
activesupport (>= 6.1.7.3, < 7.1.0)
cocoapods (~> 1.12)
cocoapods (~> 1.11, >= 1.11.2)
RUBY VERSION
ruby 3.0.6p216
ruby 2.7.4p191
BUNDLED WITH
2.4.13
2.2.27
+1 -1
View File
@@ -16,7 +16,7 @@ import * as React from 'react';
const {TestModule} = NativeModules;
class AccessibilityManagerTest extends React.Component<{...}> {
componentDidMount(): void {
componentDidMount() {
invariant(
NativeAccessibilityManager,
"NativeAccessibilityManager doesn't exist",
+1
View File
@@ -38,6 +38,7 @@ class AppEventsTest extends React.Component<{...}, State> {
NativeAppEventEmitter.addListener('testEvent', this.receiveEvent);
const event = {data: TEST_PAYLOAD, ts: Date.now()};
TestModule.sendAppEvent('testEvent', event);
// eslint-disable-next-line react/no-did-mount-set-state
this.setState({sent: event});
}
+245
View File
@@ -0,0 +1,245 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {AsyncStorage, Text, View, StyleSheet} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
const deepDiffer = require('react-native/Libraries/Utilities/differ/deepDiffer');
const nullthrows = require('nullthrows');
const DEBUG = false;
const KEY_1 = 'key_1';
const VAL_1 = 'val_1';
const KEY_2 = 'key_2';
const VAL_2 = 'val_2';
const KEY_MERGE = 'key_merge';
const VAL_MERGE_1 = {foo: 1, bar: {hoo: 1, boo: 1}, moo: {a: 3}};
const VAL_MERGE_2 = {bar: {hoo: 2}, baz: 2, moo: {a: 3}};
const VAL_MERGE_EXPECT = {foo: 1, bar: {hoo: 2, boo: 1}, baz: 2, moo: {a: 3}};
// setup in componentDidMount
let done = (result: ?boolean) => {};
let updateMessage = (message: string) => {};
function runTestCase(description: string, fn) {
updateMessage(description);
fn();
}
function expectTrue(condition: boolean, message: string) {
if (!condition) {
throw new Error(message);
}
}
// Type-safe wrapper around JSON.stringify
function stringify(
value:
| void
| null
| string
| number
| boolean
| {...}
| $ReadOnlyArray<mixed>,
): string {
if (typeof value === 'undefined') {
return 'undefined';
}
return JSON.stringify(value);
}
function expectEqual(lhs, rhs, testname: string) {
expectTrue(
!deepDiffer(lhs, rhs),
'Error in test ' +
testname +
': expected\n' +
stringify(rhs) +
'\ngot\n' +
stringify(lhs),
);
}
function expectAsyncNoError(place, err) {
if (err instanceof Error) {
err = err.message;
}
expectTrue(
err === null,
'Unexpected error in ' + place + ': ' + stringify(err),
);
}
function testSetAndGet() {
AsyncStorage.setItem(KEY_1, VAL_1, err1 => {
expectAsyncNoError('testSetAndGet/setItem', err1);
AsyncStorage.getItem(KEY_1, (err2, result) => {
expectAsyncNoError('testSetAndGet/getItem', err2);
expectEqual(result, VAL_1, 'testSetAndGet setItem');
updateMessage('get(key_1) correctly returned ' + String(result));
runTestCase('should get null for missing key', testMissingGet);
});
});
}
function testMissingGet() {
AsyncStorage.getItem(KEY_2, (err, result) => {
expectAsyncNoError('testMissingGet/setItem', err);
expectEqual(result, null, 'testMissingGet');
updateMessage('missing get(key_2) correctly returned ' + String(result));
runTestCase('check set twice results in a single key', testSetTwice);
});
}
function testSetTwice() {
AsyncStorage.setItem(KEY_1, VAL_1, () => {
AsyncStorage.setItem(KEY_1, VAL_1, () => {
AsyncStorage.getItem(KEY_1, (err, result) => {
expectAsyncNoError('testSetTwice/setItem', err);
expectEqual(result, VAL_1, 'testSetTwice');
updateMessage('setTwice worked as expected');
runTestCase('test removeItem', testRemoveItem);
});
});
});
}
function testRemoveItem() {
AsyncStorage.setItem(KEY_1, VAL_1, () => {
AsyncStorage.setItem(KEY_2, VAL_2, () => {
AsyncStorage.getAllKeys((err, result) => {
expectAsyncNoError('testRemoveItem/getAllKeys', err);
expectTrue(
nullthrows(result).indexOf(KEY_1) >= 0 &&
nullthrows(result).indexOf(KEY_2) >= 0,
'Missing KEY_1 or KEY_2 in ' + '(' + nullthrows(result).join() + ')',
);
updateMessage('testRemoveItem - add two items');
AsyncStorage.removeItem(KEY_1, err2 => {
expectAsyncNoError('testRemoveItem/removeItem', err2);
updateMessage('delete successful ');
AsyncStorage.getItem(KEY_1, (err3, result2) => {
expectAsyncNoError('testRemoveItem/getItem', err3);
expectEqual(
result2,
null,
'testRemoveItem: key_1 present after delete',
);
updateMessage('key properly removed ');
AsyncStorage.getAllKeys((err4, result3) => {
expectAsyncNoError('testRemoveItem/getAllKeys', err4);
expectTrue(
nullthrows(result3).indexOf(KEY_1) === -1,
'Unexpected: KEY_1 present in ' + nullthrows(result3).join(),
);
updateMessage('proper length returned.');
runTestCase('should merge values', testMerge);
});
});
});
});
});
});
}
function testMerge() {
AsyncStorage.setItem(KEY_MERGE, stringify(VAL_MERGE_1), err1 => {
expectAsyncNoError('testMerge/setItem', err1);
AsyncStorage.mergeItem(KEY_MERGE, stringify(VAL_MERGE_2), err2 => {
expectAsyncNoError('testMerge/mergeItem', err2);
AsyncStorage.getItem(KEY_MERGE, (err3, result) => {
expectAsyncNoError('testMerge/setItem', err3);
expectEqual(
JSON.parse(nullthrows(result)),
VAL_MERGE_EXPECT,
'testMerge',
);
updateMessage('objects deeply merged\nDone!');
runTestCase('multi set and get', testOptimizedMultiGet);
});
});
});
}
function testOptimizedMultiGet() {
let batch = [
[KEY_1, VAL_1],
[KEY_2, VAL_2],
];
let keys = batch.map(([key, value]) => key);
AsyncStorage.multiSet(batch, err1 => {
// yes, twice on purpose
[1, 2].forEach(i => {
expectAsyncNoError(`${i} testOptimizedMultiGet/multiSet`, err1);
AsyncStorage.multiGet(keys, (err2, result) => {
expectAsyncNoError(`${i} testOptimizedMultiGet/multiGet`, err2);
expectEqual(result, batch, `${i} testOptimizedMultiGet multiGet`);
updateMessage(
'multiGet([key_1, key_2]) correctly returned ' + stringify(result),
);
done();
});
});
});
}
class AsyncStorageTest extends React.Component<{...}, $FlowFixMeState> {
state: any | {|done: boolean, messages: string|} = {
messages: 'Initializing...',
done: false,
};
componentDidMount() {
done = () =>
this.setState({done: true}, () => {
TestModule.markTestCompleted();
});
updateMessage = msg => {
this.setState({messages: this.state.messages.concat('\n' + msg)});
DEBUG && console.log(msg);
};
AsyncStorage.clear(testSetAndGet);
}
render(): React.Node {
return (
<View style={styles.container}>
<Text>
{
/* $FlowFixMe[incompatible-type] (>=0.54.0 site=react_native_fb,react_
* native_oss) This comment suppresses an error found when Flow v0.54
* was deployed. To see the error delete this comment and run Flow.
*/
this.constructor.displayName + ': '
}
{this.state.done ? 'Done' : 'Testing...'}
{'\n\n' + this.state.messages}
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'white',
padding: 40,
},
});
AsyncStorageTest.displayName = 'AsyncStorageTest';
module.exports = AsyncStorageTest;
+3 -5
View File
@@ -1,4 +1,4 @@
load("@fbsource//tools/build_defs:js_library_glob.bzl", "js_library_glob")
load("@fbsource//tools/build_defs:js_glob.bzl", "js_glob")
load("@fbsource//tools/build_defs/oss:metro_defs.bzl", "rn_library")
# This file was generated by running
@@ -6,7 +6,7 @@ load("@fbsource//tools/build_defs/oss:metro_defs.bzl", "rn_library")
rn_library(
name = "IntegrationTests",
srcs = js_library_glob(
srcs = js_glob(
[
"**/*",
],
@@ -17,9 +17,7 @@ rn_library(
"websocket_integration_test_server.js",
],
),
labels = [
"pfh:ReactNative_CommonInfrastructurePlaceholder",
],
labels = ["supermodule:xplat/default/public.react_native.tests"],
skip_processors = True,
visibility = ["PUBLIC"],
deps = [
@@ -20,7 +20,7 @@ const {View} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
class GlobalEvalWithSourceUrlTest extends React.Component<{...}> {
componentDidMount(): void {
componentDidMount() {
if (typeof global.globalEvalWithSourceUrl !== 'function') {
throw new Error(
'Expected to find globalEvalWithSourceUrl function on global object but found ' +
+1 -1
View File
@@ -16,7 +16,7 @@ const {Image} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
class ImageSnapshotTest extends React.Component<{...}> {
componentDidMount(): void {
componentDidMount() {
if (!TestModule.verifySnapshot) {
throw new Error('TestModule.verifySnapshot not defined.');
}
@@ -61,7 +61,6 @@ class IntegrationTestHarnessTest extends React.Component<Props, State> {
* native_oss) This comment suppresses an error found when Flow v0.54
* was deployed. To see the error delete this comment and run Flow.
*/
// $FlowFixMe[unsafe-addition]
this.constructor.displayName + ': '
}
{this.state.done ? 'Done' : 'Testing...'}
+3 -2
View File
@@ -20,6 +20,7 @@ const {AppRegistry, ScrollView, StyleSheet, Text, TouchableOpacity, View} =
const TESTS = [
require('./IntegrationTestHarnessTest'),
require('./TimersTest'),
require('./AsyncStorageTest'),
require('./LayoutEventsTest'),
require('./AppEventsTest'),
require('./SimpleSnapshotTest'),
@@ -45,11 +46,11 @@ require('./LoggingTestModule');
type Test = any;
class IntegrationTestsApp extends React.Component<{...}, $FlowFixMeState> {
state: {test: ?Test} = {
state = {
test: (null: ?Test),
};
render(): React.Node {
render() {
if (this.state.test) {
return (
<ScrollView>
+1 -4
View File
@@ -19,7 +19,7 @@ import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet';
const deepDiffer = require('react-native/Libraries/Utilities/differ/deepDiffer');
function debug(...args: Array<void | Layout | string>) {
function debug(...args) {
// console.log.apply(null, arguments);
}
@@ -119,19 +119,16 @@ class LayoutEventsTest extends React.Component<Props, State> {
}
onViewLayout: (e: LayoutEvent) => void = (e: LayoutEvent) => {
// $FlowFixMe[incompatible-call]
debug('received view layout event\n', e.nativeEvent);
this.setState({viewLayout: e.nativeEvent.layout}, this.checkLayout);
};
onTextLayout: (e: LayoutEvent) => void = (e: LayoutEvent) => {
// $FlowFixMe[incompatible-call]
debug('received text layout event\n', e.nativeEvent);
this.setState({textLayout: e.nativeEvent.layout}, this.checkLayout);
};
onImageLayout: (e: LayoutEvent) => void = (e: LayoutEvent) => {
// $FlowFixMe[incompatible-call]
debug('received image layout event\n', e.nativeEvent);
this.setState({imageLayout: e.nativeEvent.layout}, this.checkLayout);
};
+1 -1
View File
@@ -17,7 +17,7 @@ const {StyleSheet, View} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
class SimpleSnapshotTest extends React.Component<{...}> {
componentDidMount(): void {
componentDidMount() {
if (!TestModule.verifySnapshot) {
throw new Error('TestModule.verifySnapshot not defined.');
}
+1 -1
View File
@@ -17,7 +17,7 @@ const {View} = ReactNative;
const {TestModule, RNTesterTestModule} = ReactNative.NativeModules;
class SyncMethodTest extends React.Component<{...}> {
componentDidMount(): void {
componentDidMount() {
if (
RNTesterTestModule.echoString('test string value') !== 'test string value'
) {
+4 -4
View File
@@ -39,7 +39,7 @@ class TimersTest extends React.Component<Props, State> {
};
setTimeout(fn: () => void, time: number): TimeoutID {
const id: TimeoutID = setTimeout(() => {
const id = setTimeout(() => {
this._timeoutIDs.delete(id);
fn();
}, time);
@@ -70,7 +70,7 @@ class TimersTest extends React.Component<Props, State> {
}
setImmediate(fn: () => void): ImmediateID {
const id: any = setImmediate(() => {
const id = setImmediate(() => {
this._immediateIDs.delete(id);
fn();
});
@@ -81,7 +81,7 @@ class TimersTest extends React.Component<Props, State> {
}
requestAnimationFrame(fn: () => void): AnimationFrameID {
const id: AnimationFrameID = requestAnimationFrame(() => {
const id = requestAnimationFrame(() => {
this._animationFrameIDs.delete(id);
fn();
});
@@ -253,7 +253,7 @@ class TimersTest extends React.Component<Props, State> {
);
}
_incrementInterval(): void {
_incrementInterval() {
if (this.state.count > 3) {
throw new Error('interval incremented past end.');
}
+3 -3
View File
@@ -69,11 +69,11 @@ class WebSocketTest extends React.Component<{...}, State> {
});
};
_socketIsConnected = (): boolean => {
_socketIsConnected = () => {
return this.state.socketState === 1; //'OPEN'
};
_socketIsDisconnected = (): boolean => {
_socketIsDisconnected = () => {
return this.state.socketState === 3; //'CLOSED'
};
@@ -106,7 +106,7 @@ class WebSocketTest extends React.Component<{...}, State> {
this._sendText(this.state.testMessage);
};
_receivedTestExpectedResponse = (): boolean => {
_receivedTestExpectedResponse = () => {
return this.state.lastMessage === this.state.testExpectedResponse;
};
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# Set terminal title
echo -en "\033]0;Web Socket Test Server\a"
clear
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
pushd "$THIS_DIR"
./websocket_integration_test_server.js
popd
echo "Process terminated. Press <enter> to close the window"
read
-12
View File
@@ -1,12 +0,0 @@
#!/bin/bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
pushd "$THIS_DIR" || exit
./websocket_integration_test_server.js
popd || exit
echo "Process terminated."
@@ -8,13 +8,12 @@
* @format
*/
import type {ProcessedColorValue} from '../StyleSheet/processColor';
import type {ColorValue} from '../StyleSheet/StyleSheet';
import RCTActionSheetManager from './NativeActionSheetManager';
const processColor = require('../StyleSheet/processColor').default;
const invariant = require('invariant');
const processColor = require('../StyleSheet/processColor');
import type {ColorValue} from '../StyleSheet/StyleSheet';
import type {ProcessedColorValue} from '../StyleSheet/processColor';
/**
* Display action sheets and share sheets on iOS.
@@ -144,13 +143,6 @@ const ActionSheetIOS = {
successCallback,
);
},
dismissActionSheet: () => {
invariant(RCTActionSheetManager, "ActionSheetManager doesn't exist");
if (typeof RCTActionSheetManager.dismissActionSheet === 'function') {
RCTActionSheetManager.dismissActionSheet();
}
},
};
module.exports = ActionSheetIOS;
@@ -9,7 +9,6 @@
*/
import type {TurboModule} from '../TurboModule/RCTExport';
import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
@@ -48,7 +47,6 @@ export interface Spec extends TurboModule {
|}) => void,
successCallback: (completed: boolean, activityType: ?string) => void,
) => void;
+dismissActionSheet?: () => void;
}
export default (TurboModuleRegistry.get<Spec>('ActionSheetManager'): ?Spec);
@@ -23,8 +23,8 @@ Pod::Spec.new do |s|
s.homepage = "https://reactnative.dev/"
s.documentation_url = "https://reactnative.dev/docs/actionsheetios"
s.license = package["license"]
s.author = "Meta Platforms, Inc. and its affiliates"
s.platforms = { :ios => "12.4" }
s.author = "Facebook, Inc. and its affiliates"
s.platforms = { :ios => "11.0" }
s.source = source
s.source_files = "*.{m}"
s.preserve_paths = "package.json", "LICENSE", "LICENSE-docs"
@@ -8,9 +8,8 @@
* @flow
*/
import type {DialogOptions} from '../NativeModules/specs/NativeDialogManagerAndroid';
import Platform from '../Utilities/Platform';
import type {DialogOptions} from '../NativeModules/specs/NativeDialogManagerAndroid';
import RCTAlertManager from './RCTAlertManager';
export type AlertType =
@@ -22,14 +21,12 @@ export type AlertButtonStyle = 'default' | 'cancel' | 'destructive';
export type Buttons = Array<{
text?: string,
onPress?: ?Function,
isPreferred?: boolean,
style?: AlertButtonStyle,
...
}>;
type Options = {
cancelable?: ?boolean,
userInterfaceStyle?: 'unspecified' | 'light' | 'dark',
onDismiss?: ?() => void,
...
};
@@ -47,15 +44,7 @@ class Alert {
options?: Options,
): void {
if (Platform.OS === 'ios') {
Alert.prompt(
title,
message,
buttons,
'default',
undefined,
undefined,
options,
);
Alert.prompt(title, message, buttons, 'default');
} else if (Platform.OS === 'android') {
const NativeDialogManagerAndroid =
require('../NativeModules/specs/NativeDialogManagerAndroid').default;
@@ -93,8 +82,6 @@ class Alert {
config.buttonPositive = buttonPositive.text || defaultPositiveText;
}
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by
* Flow's LTI update could not be added via codemod */
const onAction = (action, buttonKey) => {
if (action === constants.buttonClicked) {
if (buttonKey === constants.buttonNeutral) {
@@ -108,7 +95,7 @@ class Alert {
options && options.onDismiss && options.onDismiss();
}
};
const onError = (errorMessage: string) => console.warn(errorMessage);
const onError = errorMessage => console.warn(errorMessage);
NativeDialogManagerAndroid.showAlert(config, onError, onAction);
}
}
@@ -120,14 +107,12 @@ class Alert {
type?: ?AlertType = 'plain-text',
defaultValue?: string,
keyboardType?: string,
options?: Options,
): void {
if (Platform.OS === 'ios') {
let callbacks: Array<?any> = [];
let callbacks = [];
const buttons = [];
let cancelButtonKey;
let destructiveButtonKey;
let preferredButtonKey;
if (typeof callbackOrButtons === 'function') {
callbacks = [callbackOrButtons];
} else if (Array.isArray(callbackOrButtons)) {
@@ -138,11 +123,8 @@ class Alert {
} else if (btn.style === 'destructive') {
destructiveButtonKey = String(index);
}
if (btn.isPreferred) {
preferredButtonKey = String(index);
}
if (btn.text || index < (callbackOrButtons || []).length - 1) {
const btnDef: {[number]: string} = {};
const btnDef = {};
btnDef[index] = btn.text || '';
buttons.push(btnDef);
}
@@ -158,9 +140,7 @@ class Alert {
defaultValue,
cancelButtonKey,
destructiveButtonKey,
preferredButtonKey,
keyboardType,
userInterfaceStyle: options?.userInterfaceStyle || undefined,
},
(id, value) => {
const cb = callbacks[id];
@@ -9,7 +9,6 @@
*/
import type {TurboModule} from '../TurboModule/RCTExport';
import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';
export type Args = {|
@@ -20,9 +19,7 @@ export type Args = {|
defaultValue?: string,
cancelButtonKey?: string,
destructiveButtonKey?: string,
preferredButtonKey?: string,
keyboardType?: string,
userInterfaceStyle?: string,
|};
export interface Spec extends TurboModule {
@@ -8,9 +8,8 @@
* @flow strict-local
*/
import type {Args} from './NativeAlertManager';
import NativeAlertManager from './NativeAlertManager';
import type {Args} from './NativeAlertManager';
module.exports = {
alertWithArgs(
+46
View File
@@ -0,0 +1,46 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import Platform from '../Utilities/Platform';
import typeof AnimatedFlatList from './components/AnimatedFlatList';
import typeof AnimatedImage from './components/AnimatedImage';
import typeof AnimatedScrollView from './components/AnimatedScrollView';
import typeof AnimatedSectionList from './components/AnimatedSectionList';
import typeof AnimatedText from './components/AnimatedText';
import typeof AnimatedView from './components/AnimatedView';
const AnimatedMock = require('./AnimatedMock');
const AnimatedImplementation = require('./AnimatedImplementation');
const Animated = ((Platform.isTesting
? AnimatedMock
: AnimatedImplementation): typeof AnimatedMock);
module.exports = {
get FlatList(): AnimatedFlatList {
return require('./components/AnimatedFlatList');
},
get Image(): AnimatedImage {
return require('./components/AnimatedImage');
},
get ScrollView(): AnimatedScrollView {
return require('./components/AnimatedScrollView');
},
get SectionList(): AnimatedSectionList {
return require('./components/AnimatedSectionList');
},
get Text(): AnimatedText {
return require('./components/AnimatedText');
},
get View(): AnimatedView {
return require('./components/AnimatedView');
},
...Animated,
};
@@ -10,13 +10,16 @@
'use strict';
import type {PlatformConfig} from './AnimatedPlatformConfig';
const AnimatedValue = require('./nodes/AnimatedValue');
const AnimatedValueXY = require('./nodes/AnimatedValueXY');
const NativeAnimatedHelper = require('./NativeAnimatedHelper');
const ReactNative = require('../Renderer/shims/ReactNative');
import {findNodeHandle} from '../ReactNative/RendererProxy';
import NativeAnimatedHelper from './NativeAnimatedHelper';
import AnimatedValue from './nodes/AnimatedValue';
import AnimatedValueXY from './nodes/AnimatedValueXY';
import invariant from 'invariant';
const invariant = require('invariant');
const {shouldUseNativeDriver} = require('./NativeAnimatedHelper');
import type {PlatformConfig} from './AnimatedPlatformConfig';
export type Mapping =
| {[key: string]: Mapping, ...}
@@ -28,7 +31,7 @@ export type EventConfig = {
platformConfig?: PlatformConfig,
};
export function attachNativeEvent(
function attachNativeEvent(
viewRef: any,
eventName: string,
argMapping: $ReadOnlyArray<?Mapping>,
@@ -38,7 +41,7 @@ export function attachNativeEvent(
// key path inside the `nativeEvent` object. Ex.: ['contentOffset', 'x'].
const eventMappings = [];
const traverse = (value: mixed, path: Array<string>) => {
const traverse = (value, path) => {
if (value instanceof AnimatedValue) {
value.__makeNative(platformConfig);
@@ -64,7 +67,7 @@ export function attachNativeEvent(
// Assume that the event containing `nativeEvent` is always the first argument.
traverse(argMapping[0].nativeEvent, []);
const viewTag = findNodeHandle(viewRef);
const viewTag = ReactNative.findNodeHandle(viewRef);
if (viewTag != null) {
eventMappings.forEach(mapping => {
NativeAnimatedHelper.API.addAnimatedEventToView(
@@ -91,8 +94,8 @@ export function attachNativeEvent(
};
}
function validateMapping(argMapping: $ReadOnlyArray<?Mapping>, args: any) {
const validate = (recMapping: ?Mapping, recEvt: any, key: string) => {
function validateMapping(argMapping, args) {
const validate = (recMapping, recEvt, key) => {
if (recMapping instanceof AnimatedValue) {
invariant(
typeof recEvt === 'number',
@@ -143,7 +146,7 @@ function validateMapping(argMapping: $ReadOnlyArray<?Mapping>, args: any) {
});
}
export class AnimatedEvent {
class AnimatedEvent {
_argMapping: $ReadOnlyArray<?Mapping>;
_listeners: Array<Function> = [];
_attachedEvent: ?{detach: () => void, ...};
@@ -162,7 +165,7 @@ export class AnimatedEvent {
this.__addListener(config.listener);
}
this._attachedEvent = null;
this.__isNative = NativeAnimatedHelper.shouldUseNativeDriver(config);
this.__isNative = shouldUseNativeDriver(config);
this.__platformConfig = config.platformConfig;
}
@@ -174,7 +177,7 @@ export class AnimatedEvent {
this._listeners = this._listeners.filter(listener => listener !== callback);
}
__attach(viewRef: any, eventName: string): void {
__attach(viewRef: any, eventName: string) {
invariant(
this.__isNative,
'Only native driven events need to be attached.',
@@ -188,7 +191,7 @@ export class AnimatedEvent {
);
}
__detach(viewTag: any, eventName: string): void {
__detach(viewTag: any, eventName: string) {
invariant(
this.__isNative,
'Only native driven events need to be detached.',
@@ -220,10 +223,7 @@ export class AnimatedEvent {
validatedMapping = true;
}
const traverse = (
recMapping: ?(Mapping | AnimatedValue),
recEvt: any,
) => {
const traverse = (recMapping, recEvt) => {
if (recMapping instanceof AnimatedValue) {
if (typeof recEvt === 'number') {
recMapping.setValue(recEvt);
@@ -254,3 +254,5 @@ export class AnimatedEvent {
this._listeners.forEach(listener => listener(...args));
};
}
module.exports = {AnimatedEvent, attachNativeEvent};
@@ -10,33 +10,35 @@
'use strict';
import type {EventConfig, Mapping} from './AnimatedEvent';
const {AnimatedEvent, attachNativeEvent} = require('./AnimatedEvent');
const AnimatedAddition = require('./nodes/AnimatedAddition');
const AnimatedDiffClamp = require('./nodes/AnimatedDiffClamp');
const AnimatedDivision = require('./nodes/AnimatedDivision');
const AnimatedInterpolation = require('./nodes/AnimatedInterpolation');
const AnimatedModulo = require('./nodes/AnimatedModulo');
const AnimatedMultiplication = require('./nodes/AnimatedMultiplication');
const AnimatedNode = require('./nodes/AnimatedNode');
const AnimatedSubtraction = require('./nodes/AnimatedSubtraction');
const AnimatedTracking = require('./nodes/AnimatedTracking');
const AnimatedValue = require('./nodes/AnimatedValue');
const AnimatedValueXY = require('./nodes/AnimatedValueXY');
const DecayAnimation = require('./animations/DecayAnimation');
const SpringAnimation = require('./animations/SpringAnimation');
const TimingAnimation = require('./animations/TimingAnimation');
const createAnimatedComponent = require('./createAnimatedComponent');
import type {
AnimationConfig,
EndCallback,
EndResult,
} from './animations/Animation';
import type {TimingAnimationConfig} from './animations/TimingAnimation';
import type {DecayAnimationConfig} from './animations/DecayAnimation';
import type {SpringAnimationConfig} from './animations/SpringAnimation';
import type {TimingAnimationConfig} from './animations/TimingAnimation';
import type {Mapping, EventConfig} from './AnimatedEvent';
import {AnimatedEvent, attachNativeEvent} from './AnimatedEvent';
import DecayAnimation from './animations/DecayAnimation';
import SpringAnimation from './animations/SpringAnimation';
import TimingAnimation from './animations/TimingAnimation';
import createAnimatedComponent from './createAnimatedComponent';
import AnimatedAddition from './nodes/AnimatedAddition';
import AnimatedColor from './nodes/AnimatedColor';
import AnimatedDiffClamp from './nodes/AnimatedDiffClamp';
import AnimatedDivision from './nodes/AnimatedDivision';
import AnimatedInterpolation from './nodes/AnimatedInterpolation';
import AnimatedModulo from './nodes/AnimatedModulo';
import AnimatedMultiplication from './nodes/AnimatedMultiplication';
import AnimatedNode from './nodes/AnimatedNode';
import AnimatedSubtraction from './nodes/AnimatedSubtraction';
import AnimatedTracking from './nodes/AnimatedTracking';
import AnimatedValue from './nodes/AnimatedValue';
import AnimatedValueXY from './nodes/AnimatedValueXY';
export type CompositeAnimation = {
start: (callback?: ?EndCallback) => void,
@@ -89,10 +91,10 @@ const diffClamp = function (
const _combineCallbacks = function (
callback: ?EndCallback,
config: $ReadOnly<{...AnimationConfig, ...}>,
config: {...AnimationConfig, ...},
) {
if (callback && config.onComplete) {
return (...args: Array<EndResult>) => {
return (...args) => {
config.onComplete && config.onComplete(...args);
callback && callback(...args);
};
@@ -306,7 +308,7 @@ const sequence = function (
let current = 0;
return {
start: function (callback?: ?EndCallback) {
const onComplete = function (result: EndResult) {
const onComplete = function (result) {
if (!result.finished) {
callback && callback(result);
return;
@@ -367,7 +369,7 @@ const parallel = function (
): CompositeAnimation {
let doneCount = 0;
// Make sure we only call stop() at most once for each animation
const hasEnded: {[number]: boolean} = {};
const hasEnded = {};
const stopTogether = !(config && config.stopTogether === false);
const result = {
@@ -378,7 +380,7 @@ const parallel = function (
}
animations.forEach((animation, idx) => {
const cb = function (endResult: EndResult | {finished: boolean}) {
const cb = function (endResult) {
hasEnded[idx] = true;
doneCount++;
if (doneCount === animations.length) {
@@ -415,7 +417,7 @@ const parallel = function (
});
},
_startNativeLoop: function (): empty {
_startNativeLoop: function () {
throw new Error(
'Loops run using the native driver cannot contain Animated.parallel animations',
);
@@ -458,7 +460,6 @@ type LoopAnimationConfig = {
const loop = function (
animation: CompositeAnimation,
// $FlowFixMe[prop-missing]
{iterations = -1, resetBeforeIteration = true}: LoopAnimationConfig = {},
): CompositeAnimation {
let isFinished = false;
@@ -550,19 +551,6 @@ const event = function (
}
};
// All types of animated nodes that represent scalar numbers and can be interpolated (etc)
type AnimatedNumeric =
| AnimatedAddition
| AnimatedDiffClamp
| AnimatedDivision
| AnimatedInterpolation<number>
| AnimatedModulo
| AnimatedMultiplication
| AnimatedSubtraction
| AnimatedValue;
export type {AnimatedNumeric as Numeric};
/**
* The `Animated` library is designed to make animations fluid, powerful, and
* easy to build and maintain. `Animated` focuses on declarative relationships
@@ -573,7 +561,7 @@ export type {AnimatedNumeric as Numeric};
*
* See https://reactnative.dev/docs/animated
*/
export default {
module.exports = {
/**
* Standard value class for driving animations. Typically initialized with
* `new Animated.Value(0);`
@@ -10,21 +10,21 @@
'use strict';
import type {Numeric as AnimatedNumeric} from './AnimatedImplementation';
import type {EndResult} from './animations/Animation';
const {AnimatedEvent, attachNativeEvent} = require('./AnimatedEvent');
const AnimatedImplementation = require('./AnimatedImplementation');
const AnimatedInterpolation = require('./nodes/AnimatedInterpolation');
const AnimatedNode = require('./nodes/AnimatedNode');
const AnimatedValue = require('./nodes/AnimatedValue');
const AnimatedValueXY = require('./nodes/AnimatedValueXY');
const createAnimatedComponent = require('./createAnimatedComponent');
import type {EndCallback} from './animations/Animation';
import type {TimingAnimationConfig} from './animations/TimingAnimation';
import type {DecayAnimationConfig} from './animations/DecayAnimation';
import type {SpringAnimationConfig} from './animations/SpringAnimation';
import type {TimingAnimationConfig} from './animations/TimingAnimation';
import {AnimatedEvent, attachNativeEvent} from './AnimatedEvent';
import AnimatedImplementation from './AnimatedImplementation';
import createAnimatedComponent from './createAnimatedComponent';
import AnimatedColor from './nodes/AnimatedColor';
import AnimatedInterpolation from './nodes/AnimatedInterpolation';
import AnimatedNode from './nodes/AnimatedNode';
import AnimatedValue from './nodes/AnimatedValue';
import AnimatedValueXY from './nodes/AnimatedValueXY';
/**
* Animations are a source of flakiness in snapshot testing. This mock replaces
@@ -43,7 +43,7 @@ function mockAnimationStart(
const guardedCallback =
callback == null
? callback
: (...args: Array<EndResult>) => {
: (...args) => {
if (inAnimationCallback) {
console.warn(
'Ignoring recursive animation callback when running mock animations',
@@ -158,15 +158,12 @@ type LoopAnimationConfig = {
const loop = function (
animation: CompositeAnimation,
// $FlowFixMe[prop-missing]
{iterations = -1}: LoopAnimationConfig = {},
): CompositeAnimation {
return emptyAnimation;
};
export type {AnimatedNumeric as Numeric};
export default {
module.exports = {
Value: AnimatedValue,
ValueXY: AnimatedValueXY,
Color: AnimatedColor,
@@ -10,9 +10,9 @@
'use strict';
import AnimatedImplementation from './AnimatedImplementation';
const AnimatedImplementation = require('./AnimatedImplementation');
export default {
module.exports = {
...AnimatedImplementation,
/* $FlowFixMe[incompatible-call] createAnimatedComponent expects to receive
* types. Plain intrinsic components can't be typed like this */
@@ -214,7 +214,7 @@ const Easing = {
x2: number,
y2: number,
): (t: number) => number {
const _bezier = require('./bezier').default;
const _bezier = require('./bezier');
return _bezier(x1, y1, x2, y2);
},
@@ -247,4 +247,4 @@ const Easing = {
},
};
export default Easing;
module.exports = Easing;
+411
View File
@@ -0,0 +1,411 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import NativeAnimatedNonTurboModule from './NativeAnimatedModule';
import NativeAnimatedTurboModule from './NativeAnimatedTurboModule';
import NativeEventEmitter from '../EventEmitter/NativeEventEmitter';
import Platform from '../Utilities/Platform';
import type {EventConfig} from './AnimatedEvent';
import type {
EventMapping,
AnimatedNodeConfig,
AnimatingNodeConfig,
} from './NativeAnimatedModule';
import type {AnimationConfig, EndCallback} from './animations/Animation';
import type {InterpolationConfigType} from './nodes/AnimatedInterpolation';
import invariant from 'invariant';
// TODO T69437152 @petetheheat - Delete this fork when Fabric ships to 100%.
const NativeAnimatedModule =
Platform.OS === 'ios' && global.RN$Bridgeless === true
? NativeAnimatedTurboModule
: NativeAnimatedNonTurboModule;
let __nativeAnimatedNodeTagCount = 1; /* used for animated nodes */
let __nativeAnimationIdCount = 1; /* used for started animations */
let nativeEventEmitter;
let waitingForQueuedOperations = new Set();
let queueOperations = false;
let queue: Array<() => void> = [];
/**
* Simple wrappers around NativeAnimatedModule to provide flow and autocomplete support for
* the native module methods
*/
const API = {
getValue: function (
tag: number,
saveValueCallback: (value: number) => void,
): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
API.queueOperation(() => {
NativeAnimatedModule.getValue(tag, saveValueCallback);
});
},
setWaitingForIdentifier: function (id: string): void {
waitingForQueuedOperations.add(id);
queueOperations = true;
},
unsetWaitingForIdentifier: function (id: string): void {
waitingForQueuedOperations.delete(id);
if (waitingForQueuedOperations.size === 0) {
queueOperations = false;
API.disableQueue();
}
},
disableQueue: function (): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
if (Platform.OS === 'android') {
NativeAnimatedModule.startOperationBatch();
}
for (let q = 0, l = queue.length; q < l; q++) {
queue[q]();
}
queue.length = 0;
if (Platform.OS === 'android') {
NativeAnimatedModule.finishOperationBatch();
}
},
queueOperation: (fn: () => void): void => {
if (queueOperations) {
queue.push(fn);
} else {
fn();
}
},
createAnimatedNode: function (tag: number, config: AnimatedNodeConfig): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
API.queueOperation(() =>
NativeAnimatedModule.createAnimatedNode(tag, config),
);
},
startListeningToAnimatedNodeValue: function (tag: number) {
invariant(NativeAnimatedModule, 'Native animated module is not available');
API.queueOperation(() =>
NativeAnimatedModule.startListeningToAnimatedNodeValue(tag),
);
},
stopListeningToAnimatedNodeValue: function (tag: number) {
invariant(NativeAnimatedModule, 'Native animated module is not available');
API.queueOperation(() =>
NativeAnimatedModule.stopListeningToAnimatedNodeValue(tag),
);
},
connectAnimatedNodes: function (parentTag: number, childTag: number): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
API.queueOperation(() =>
NativeAnimatedModule.connectAnimatedNodes(parentTag, childTag),
);
},
disconnectAnimatedNodes: function (
parentTag: number,
childTag: number,
): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
API.queueOperation(() =>
NativeAnimatedModule.disconnectAnimatedNodes(parentTag, childTag),
);
},
startAnimatingNode: function (
animationId: number,
nodeTag: number,
config: AnimatingNodeConfig,
endCallback: EndCallback,
): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
API.queueOperation(() =>
NativeAnimatedModule.startAnimatingNode(
animationId,
nodeTag,
config,
endCallback,
),
);
},
stopAnimation: function (animationId: number) {
invariant(NativeAnimatedModule, 'Native animated module is not available');
API.queueOperation(() => NativeAnimatedModule.stopAnimation(animationId));
},
setAnimatedNodeValue: function (nodeTag: number, value: number): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
API.queueOperation(() =>
NativeAnimatedModule.setAnimatedNodeValue(nodeTag, value),
);
},
setAnimatedNodeOffset: function (nodeTag: number, offset: number): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
API.queueOperation(() =>
NativeAnimatedModule.setAnimatedNodeOffset(nodeTag, offset),
);
},
flattenAnimatedNodeOffset: function (nodeTag: number): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
API.queueOperation(() =>
NativeAnimatedModule.flattenAnimatedNodeOffset(nodeTag),
);
},
extractAnimatedNodeOffset: function (nodeTag: number): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
API.queueOperation(() =>
NativeAnimatedModule.extractAnimatedNodeOffset(nodeTag),
);
},
connectAnimatedNodeToView: function (nodeTag: number, viewTag: number): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
API.queueOperation(() =>
NativeAnimatedModule.connectAnimatedNodeToView(nodeTag, viewTag),
);
},
disconnectAnimatedNodeFromView: function (
nodeTag: number,
viewTag: number,
): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
API.queueOperation(() =>
NativeAnimatedModule.disconnectAnimatedNodeFromView(nodeTag, viewTag),
);
},
restoreDefaultValues: function (nodeTag: number): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
// Backwards compat with older native runtimes, can be removed later.
if (NativeAnimatedModule.restoreDefaultValues != null) {
API.queueOperation(() =>
NativeAnimatedModule.restoreDefaultValues(nodeTag),
);
}
},
dropAnimatedNode: function (tag: number): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
API.queueOperation(() => NativeAnimatedModule.dropAnimatedNode(tag));
},
addAnimatedEventToView: function (
viewTag: number,
eventName: string,
eventMapping: EventMapping,
) {
invariant(NativeAnimatedModule, 'Native animated module is not available');
API.queueOperation(() =>
NativeAnimatedModule.addAnimatedEventToView(
viewTag,
eventName,
eventMapping,
),
);
},
removeAnimatedEventFromView(
viewTag: number,
eventName: string,
animatedNodeTag: number,
) {
invariant(NativeAnimatedModule, 'Native animated module is not available');
API.queueOperation(() =>
NativeAnimatedModule.removeAnimatedEventFromView(
viewTag,
eventName,
animatedNodeTag,
),
);
},
};
/**
* Styles allowed by the native animated implementation.
*
* In general native animated implementation should support any numeric property that doesn't need
* to be updated through the shadow view hierarchy (all non-layout properties).
*/
const SUPPORTED_STYLES = {
opacity: true,
transform: true,
borderRadius: true,
borderBottomEndRadius: true,
borderBottomLeftRadius: true,
borderBottomRightRadius: true,
borderBottomStartRadius: true,
borderTopEndRadius: true,
borderTopLeftRadius: true,
borderTopRightRadius: true,
borderTopStartRadius: true,
elevation: true,
zIndex: true,
/* ios styles */
shadowOpacity: true,
shadowRadius: true,
/* legacy android transform properties */
scaleX: true,
scaleY: true,
translateX: true,
translateY: true,
};
const SUPPORTED_TRANSFORMS = {
translateX: true,
translateY: true,
scale: true,
scaleX: true,
scaleY: true,
rotate: true,
rotateX: true,
rotateY: true,
rotateZ: true,
perspective: true,
};
const SUPPORTED_INTERPOLATION_PARAMS = {
inputRange: true,
outputRange: true,
extrapolate: true,
extrapolateRight: true,
extrapolateLeft: true,
};
function addWhitelistedStyleProp(prop: string): void {
SUPPORTED_STYLES[prop] = true;
}
function addWhitelistedTransformProp(prop: string): void {
SUPPORTED_TRANSFORMS[prop] = true;
}
function addWhitelistedInterpolationParam(param: string): void {
SUPPORTED_INTERPOLATION_PARAMS[param] = true;
}
function validateTransform(
configs: Array<
| {
type: 'animated',
property: string,
nodeTag: ?number,
...
}
| {
type: 'static',
property: string,
value: number | string,
...
},
>,
): void {
configs.forEach(config => {
if (!SUPPORTED_TRANSFORMS.hasOwnProperty(config.property)) {
throw new Error(
`Property '${config.property}' is not supported by native animated module`,
);
}
});
}
function validateStyles(styles: {[key: string]: ?number, ...}): void {
for (const key in styles) {
if (!SUPPORTED_STYLES.hasOwnProperty(key)) {
throw new Error(
`Style property '${key}' is not supported by native animated module`,
);
}
}
}
function validateInterpolation(config: InterpolationConfigType): void {
for (const key in config) {
if (!SUPPORTED_INTERPOLATION_PARAMS.hasOwnProperty(key)) {
throw new Error(
`Interpolation property '${key}' is not supported by native animated module`,
);
}
}
}
function generateNewNodeTag(): number {
return __nativeAnimatedNodeTagCount++;
}
function generateNewAnimationId(): number {
return __nativeAnimationIdCount++;
}
function assertNativeAnimatedModule(): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
}
let _warnedMissingNativeAnimated = false;
function shouldUseNativeDriver(
config: {...AnimationConfig, ...} | EventConfig,
): boolean {
if (config.useNativeDriver == null) {
console.warn(
'Animated: `useNativeDriver` was not specified. This is a required ' +
'option and must be explicitly set to `true` or `false`',
);
}
if (config.useNativeDriver === true && !NativeAnimatedModule) {
if (!_warnedMissingNativeAnimated) {
console.warn(
'Animated: `useNativeDriver` is not supported because the native ' +
'animated module is missing. Falling back to JS-based animation. To ' +
'resolve this, add `RCTAnimation` module to this app, or remove ' +
'`useNativeDriver`. ' +
'Make sure to run `bundle exec pod install` first. Read more about autolinking: https://github.com/react-native-community/cli/blob/master/docs/autolinking.md',
);
_warnedMissingNativeAnimated = true;
}
return false;
}
return config.useNativeDriver || false;
}
function transformDataType(value: number | string): number | string {
// Change the string type to number type so we can reuse the same logic in
// iOS and Android platform
if (typeof value !== 'string') {
return value;
}
if (/deg$/.test(value)) {
const degrees = parseFloat(value) || 0;
const radians = (degrees * Math.PI) / 180.0;
return radians;
} else {
return value;
}
}
module.exports = {
API,
addWhitelistedStyleProp,
addWhitelistedTransformProp,
addWhitelistedInterpolationParam,
validateStyles,
validateTransform,
validateInterpolation,
generateNewNodeTag,
generateNewAnimationId,
assertNativeAnimatedModule,
shouldUseNativeDriver,
transformDataType,
// $FlowExpectedError[unsafe-getters-setters] - unsafe getter lint suppresion
// $FlowExpectedError[missing-type-arg] - unsafe getter lint suppresion
get nativeEventEmitter(): NativeEventEmitter {
if (!nativeEventEmitter) {
nativeEventEmitter = new NativeEventEmitter(
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
// If you want to use the native module on other platforms, please remove this condition and test its behavior
Platform.OS !== 'ios' ? null : NativeAnimatedModule,
);
}
return nativeEventEmitter;
},
};
@@ -9,7 +9,6 @@
*/
import type {TurboModule} from '../TurboModule/RCTExport';
import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';
type EndResult = {finished: boolean, ...};
@@ -30,7 +29,6 @@ export interface Spec extends TurboModule {
+startOperationBatch: () => void;
+finishOperationBatch: () => void;
+createAnimatedNode: (tag: number, config: AnimatedNodeConfig) => void;
+updateAnimatedNodeConfig?: (tag: number, config: AnimatedNodeConfig) => void;
+getValue: (tag: number, saveValueCallback: SaveValueCallback) => void;
+startListeningToAnimatedNodeValue: (tag: number) => void;
+stopListeningToAnimatedNodeValue: (tag: number) => void;
@@ -65,9 +63,6 @@ export interface Spec extends TurboModule {
// Events
+addListener: (eventName: string) => void;
+removeListeners: (count: number) => void;
// All of the above in a batched mode
+queueAndExecuteBatchedOperations?: (operationsAndArgs: Array<any>) => void;
}
export default (TurboModuleRegistry.get<Spec>('NativeAnimatedModule'): ?Spec);
@@ -9,7 +9,6 @@
*/
import type {TurboModule} from '../TurboModule/RCTExport';
import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';
type EndResult = {finished: boolean, ...};
@@ -30,7 +29,6 @@ export interface Spec extends TurboModule {
+startOperationBatch: () => void;
+finishOperationBatch: () => void;
+createAnimatedNode: (tag: number, config: AnimatedNodeConfig) => void;
+updateAnimatedNodeConfig?: (tag: number, config: AnimatedNodeConfig) => void;
+getValue: (tag: number, saveValueCallback: SaveValueCallback) => void;
+startListeningToAnimatedNodeValue: (tag: number) => void;
+stopListeningToAnimatedNodeValue: (tag: number) => void;
@@ -65,9 +63,6 @@ export interface Spec extends TurboModule {
// Events
+addListener: (eventName: string) => void;
+removeListeners: (count: number) => void;
// All of the above in a batched mode
+queueAndExecuteBatchedOperations?: (operationsAndArgs: Array<any>) => void;
}
export default (TurboModuleRegistry.get<Spec>(
@@ -24,7 +24,7 @@ function dampingFromOrigamiValue(oValue: number) {
return (oValue - 8) * 3 + 25;
}
export function fromOrigamiTensionAndFriction(
function fromOrigamiTensionAndFriction(
tension: number,
friction: number,
): SpringConfigType {
@@ -34,7 +34,7 @@ export function fromOrigamiTensionAndFriction(
};
}
export function fromBouncinessAndSpeed(
function fromBouncinessAndSpeed(
bounciness: number,
speed: number,
): SpringConfigType {
@@ -96,3 +96,8 @@ export function fromBouncinessAndSpeed(
damping: dampingFromOrigamiValue(bouncyFriction),
};
}
module.exports = {
fromOrigamiTensionAndFriction,
fromBouncinessAndSpeed,
};
@@ -5,16 +5,13 @@
* LICENSE file in the root directory of this source tree.
*
* @format
* @oncall react_native
* @emails oncall+react_native
*/
import AnimatedProps from '../nodes/AnimatedProps';
import TestRenderer from 'react-test-renderer';
import * as React from 'react';
const {PlatformColor} = require('../../StyleSheet/PlatformColorValueTypes');
let Animated = require('../Animated').default;
const AnimatedProps = require('../nodes/AnimatedProps').default;
const TestRenderer = require('react-test-renderer');
jest.mock('../../BatchedBridge/NativeModules', () => ({
NativeAnimatedModule: {},
PlatformConstants: {
@@ -24,6 +21,8 @@ jest.mock('../../BatchedBridge/NativeModules', () => ({
},
}));
let Animated = require('../Animated');
describe('Animated tests', () => {
beforeEach(() => {
jest.resetModules();
@@ -32,10 +31,6 @@ describe('Animated tests', () => {
describe('Animated', () => {
it('works end to end', () => {
const anim = new Animated.Value(0);
const translateAnim = anim.interpolate({
inputRange: [0, 1],
outputRange: [100, 200],
});
const callback = jest.fn();
@@ -46,10 +41,10 @@ describe('Animated tests', () => {
opacity: anim,
transform: [
{
translate: [translateAnim, translateAnim],
},
{
translateX: translateAnim,
translateX: anim.interpolate({
inputRange: [0, 1],
outputRange: [100, 200],
}),
},
{scale: anim},
],
@@ -66,7 +61,7 @@ describe('Animated tests', () => {
style: {
backgroundColor: 'red',
opacity: 0,
transform: [{translate: [100, 100]}, {translateX: 100}, {scale: 0}],
transform: [{translateX: 100}, {scale: 0}],
shadowOffset: {
width: 0,
height: 0,
@@ -88,7 +83,7 @@ describe('Animated tests', () => {
style: {
backgroundColor: 'red',
opacity: 0.5,
transform: [{translate: [150, 150]}, {translateX: 150}, {scale: 0.5}],
transform: [{translateX: 150}, {scale: 0.5}],
shadowOffset: {
width: 0.5,
height: 0.5,
@@ -176,13 +171,11 @@ describe('Animated tests', () => {
expect(testRenderer.toJSON().props.style.opacity).toEqual(0);
TestRenderer.act(() => {
Animated.timing(opacity, {
toValue: 1,
duration: 0,
useNativeDriver: false,
}).start();
});
Animated.timing(opacity, {
toValue: 1,
duration: 0,
useNativeDriver: false,
}).start();
expect(testRenderer.toJSON().props.style.opacity).toEqual(1);
});
@@ -699,7 +692,7 @@ describe('Animated tests', () => {
beforeEach(() => {
jest.mock('../../Interaction/InteractionManager');
Animated = require('../Animated').default;
Animated = require('../Animated');
InteractionManager = require('../../Interaction/InteractionManager');
});
@@ -1040,10 +1033,10 @@ describe('Animated tests', () => {
});
node.__attach();
expect(callback).not.toHaveBeenCalled();
expect(callback.mock.calls.length).toBe(0);
color.setValue({r: 11, g: 22, b: 33, a: 0.5});
expect(callback).toHaveBeenCalledTimes(5);
expect(callback.mock.calls.length).toBe(4);
expect(node.__getValue()).toEqual({
style: {
backgroundColor: 'rgba(11, 22, 33, 0.5)',
@@ -1053,7 +1046,7 @@ describe('Animated tests', () => {
node.__detach();
color.setValue({r: 255, g: 0, b: 0, a: 1.0});
expect(callback).toHaveBeenCalledTimes(5);
expect(callback.mock.calls.length).toBe(4);
});
it('should track colors', () => {
@@ -1089,33 +1082,5 @@ describe('Animated tests', () => {
jest.runAllTimers();
expect(color2.__getValue()).toEqual('rgba(44, 55, 66, 0)');
});
it('should provide updates for native colors', () => {
const color = new Animated.Color('red');
const listener = jest.fn();
color.addListener(listener);
const callback = jest.fn(() => {
console.log('callback', color.__getValue());
});
const node = new AnimatedProps({style: {color}}, callback);
node.__attach();
color.setValue('blue');
expect(callback).toHaveBeenCalledTimes(5);
expect(listener).toHaveBeenCalledTimes(1);
expect(listener).toHaveBeenCalledWith({value: 'rgba(0, 0, 255, 1)'});
expect(color.__getValue()).toEqual('rgba(0, 0, 255, 1)');
callback.mockClear();
listener.mockClear();
color.setValue(PlatformColor('bar'));
expect(callback).toHaveBeenCalledTimes(1);
expect(listener).toHaveBeenCalledTimes(1);
expect(listener).toHaveBeenCalledWith({value: PlatformColor('bar')});
expect(color.__getValue()).toEqual(PlatformColor('bar'));
});
});
});
@@ -5,13 +5,13 @@
* LICENSE file in the root directory of this source tree.
*
* @format
* @oncall react_native
* @emails oncall+react_native
*/
'use strict';
import AnimatedImplementation from '../AnimatedImplementation';
import AnimatedMock from '../AnimatedMock';
const AnimatedMock = require('../AnimatedMock');
const AnimatedImplementation = require('../AnimatedImplementation');
describe('Animated Mock', () => {
it('matches implementation keys', () => {
@@ -0,0 +1,999 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @emails oncall+react_native
*/
jest
.clearAllMocks()
.mock('../../BatchedBridge/NativeModules', () => ({
NativeAnimatedModule: {},
PlatformConstants: {
getConstants() {
return {};
},
},
}))
.mock('../NativeAnimatedModule')
.mock('../../EventEmitter/NativeEventEmitter')
// findNodeHandle is imported from ReactNative so mock that whole module.
.setMock('../../Renderer/shims/ReactNative', {findNodeHandle: () => 1});
import TestRenderer from 'react-test-renderer';
import * as React from 'react';
const Animated = require('../Animated');
const NativeAnimatedHelper = require('../NativeAnimatedHelper');
describe('Native Animated', () => {
const NativeAnimatedModule = require('../NativeAnimatedModule').default;
beforeEach(() => {
Object.assign(NativeAnimatedModule, {
getValue: jest.fn(),
addAnimatedEventToView: jest.fn(),
connectAnimatedNodes: jest.fn(),
connectAnimatedNodeToView: jest.fn(),
createAnimatedNode: jest.fn(),
disconnectAnimatedNodeFromView: jest.fn(),
disconnectAnimatedNodes: jest.fn(),
dropAnimatedNode: jest.fn(),
extractAnimatedNodeOffset: jest.fn(),
flattenAnimatedNodeOffset: jest.fn(),
removeAnimatedEventFromView: jest.fn(),
restoreDefaultValues: jest.fn(),
setAnimatedNodeOffset: jest.fn(),
setAnimatedNodeValue: jest.fn(),
startAnimatingNode: jest.fn(),
startListeningToAnimatedNodeValue: jest.fn(),
stopAnimation: jest.fn(),
stopListeningToAnimatedNodeValue: jest.fn(),
});
});
describe('Animated Value', () => {
it('proxies `setValue` correctly', () => {
const opacity = new Animated.Value(0);
const ref = React.createRef(null);
Animated.timing(opacity, {
toValue: 10,
duration: 1000,
useNativeDriver: true,
}).start();
TestRenderer.create(<Animated.View ref={ref} style={{opacity}} />);
expect(ref.current).not.toBeNull();
jest.spyOn(ref.current, 'setNativeProps');
opacity.setValue(0.5);
expect(NativeAnimatedModule.setAnimatedNodeValue).toBeCalledWith(
expect.any(Number),
0.5,
);
expect(ref.current.setNativeProps).not.toHaveBeenCalled();
});
it('should set offset', () => {
const opacity = new Animated.Value(0);
opacity.setOffset(10);
opacity.__makeNative();
TestRenderer.create(<Animated.View style={{opacity}} />);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'value', value: 0, offset: 10},
);
opacity.setOffset(20);
expect(NativeAnimatedModule.setAnimatedNodeOffset).toBeCalledWith(
expect.any(Number),
20,
);
});
it('should flatten offset', () => {
const opacity = new Animated.Value(0);
opacity.__makeNative();
TestRenderer.create(<Animated.View style={{opacity}} />);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'value', value: 0, offset: 0},
);
opacity.flattenOffset();
expect(NativeAnimatedModule.flattenAnimatedNodeOffset).toBeCalledWith(
expect.any(Number),
);
});
it('should save value on unmount', () => {
NativeAnimatedModule.getValue = jest.fn((tag, saveCallback) => {
saveCallback(1);
});
const opacity = new Animated.Value(0);
opacity.__makeNative();
const root = TestRenderer.create(<Animated.View style={{opacity}} />);
const tag = opacity.__getNativeTag();
root.unmount();
expect(NativeAnimatedModule.getValue).toBeCalledWith(
tag,
expect.any(Function),
);
expect(opacity.__getValue()).toBe(1);
});
it('should deduct offset when saving value on unmount', () => {
NativeAnimatedModule.getValue = jest.fn((tag, saveCallback) => {
// Assume current raw value of value node is 0.5, the NativeAnimated
// getValue API returns the sum of raw value and offset, so return 1.
saveCallback(1);
});
const opacity = new Animated.Value(0);
opacity.setOffset(0.5);
opacity.__makeNative();
const root = TestRenderer.create(<Animated.View style={{opacity}} />);
const tag = opacity.__getNativeTag();
root.unmount();
expect(NativeAnimatedModule.getValue).toBeCalledWith(
tag,
expect.any(Function),
);
expect(opacity.__getValue()).toBe(1);
});
it('should extract offset', () => {
const opacity = new Animated.Value(0);
opacity.__makeNative();
TestRenderer.create(<Animated.View style={{opacity}} />);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'value', value: 0, offset: 0},
);
opacity.extractOffset();
expect(NativeAnimatedModule.extractAnimatedNodeOffset).toBeCalledWith(
expect.any(Number),
);
});
});
describe('Animated Listeners', () => {
it('should get updates', () => {
const value1 = new Animated.Value(0);
value1.__makeNative();
const listener = jest.fn();
const id = value1.addListener(listener);
expect(
NativeAnimatedModule.startListeningToAnimatedNodeValue,
).toHaveBeenCalledWith(value1.__getNativeTag());
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
value: 42,
tag: value1.__getNativeTag(),
});
expect(listener).toHaveBeenCalledTimes(1);
expect(listener).toBeCalledWith({value: 42});
expect(value1.__getValue()).toBe(42);
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
value: 7,
tag: value1.__getNativeTag(),
});
expect(listener).toHaveBeenCalledTimes(2);
expect(listener).toBeCalledWith({value: 7});
expect(value1.__getValue()).toBe(7);
value1.removeListener(id);
expect(
NativeAnimatedModule.stopListeningToAnimatedNodeValue,
).toHaveBeenCalledWith(value1.__getNativeTag());
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
value: 1492,
tag: value1.__getNativeTag(),
});
expect(listener).toHaveBeenCalledTimes(2);
expect(value1.__getValue()).toBe(7);
});
it('should removeAll', () => {
const value1 = new Animated.Value(0);
value1.__makeNative();
const listener = jest.fn();
[1, 2, 3, 4].forEach(() => value1.addListener(listener));
expect(
NativeAnimatedModule.startListeningToAnimatedNodeValue,
).toHaveBeenCalledWith(value1.__getNativeTag());
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
value: 42,
tag: value1.__getNativeTag(),
});
expect(listener).toHaveBeenCalledTimes(4);
expect(listener).toBeCalledWith({value: 42});
value1.removeAllListeners();
expect(
NativeAnimatedModule.stopListeningToAnimatedNodeValue,
).toHaveBeenCalledWith(value1.__getNativeTag());
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
value: 7,
tag: value1.__getNativeTag(),
});
expect(listener).toHaveBeenCalledTimes(4);
});
});
describe('Animated Events', () => {
it('should map events', () => {
const value = new Animated.Value(0);
value.__makeNative();
const event = Animated.event([{nativeEvent: {state: {foo: value}}}], {
useNativeDriver: true,
});
const root = TestRenderer.create(<Animated.View onTouchMove={event} />);
expect(NativeAnimatedModule.addAnimatedEventToView).toBeCalledWith(
expect.any(Number),
'onTouchMove',
{
nativeEventPath: ['state', 'foo'],
animatedValueTag: value.__getNativeTag(),
},
);
expect(
NativeAnimatedModule.removeAnimatedEventFromView,
).not.toHaveBeenCalled();
root.unmount();
expect(NativeAnimatedModule.removeAnimatedEventFromView).toBeCalledWith(
expect.any(Number),
'onTouchMove',
value.__getNativeTag(),
);
});
it('shoud map AnimatedValueXY', () => {
const value = new Animated.ValueXY({x: 0, y: 0});
value.__makeNative();
const event = Animated.event([{nativeEvent: {state: value}}], {
useNativeDriver: true,
});
TestRenderer.create(<Animated.View onTouchMove={event} />);
['x', 'y'].forEach((key, idx) =>
expect(
NativeAnimatedModule.addAnimatedEventToView,
).toHaveBeenNthCalledWith(idx + 1, expect.any(Number), 'onTouchMove', {
nativeEventPath: ['state', key],
animatedValueTag: value[key].__getNativeTag(),
}),
);
});
it('should throw on invalid event path', () => {
const value = new Animated.Value(0);
value.__makeNative();
const event = Animated.event([{notNativeEvent: {foo: value}}], {
useNativeDriver: true,
});
jest.spyOn(console, 'error').mockImplementationOnce((...args) => {
if (args[0].startsWith('The above error occurred in the')) {
return;
}
console.errorDebug(...args);
});
expect(() => {
TestRenderer.create(<Animated.View onTouchMove={event} />);
}).toThrowError(/nativeEvent/);
expect(NativeAnimatedModule.addAnimatedEventToView).not.toBeCalled();
console.error.mockRestore();
});
it('should call listeners', () => {
const value = new Animated.Value(0);
value.__makeNative();
const listener = jest.fn();
const event = Animated.event([{nativeEvent: {foo: value}}], {
useNativeDriver: true,
listener,
});
const handler = event.__getHandler();
handler({nativeEvent: {foo: 42}});
expect(listener).toHaveBeenCalledTimes(1);
expect(listener).toBeCalledWith({nativeEvent: {foo: 42}});
});
});
describe('Animated Graph', () => {
it('creates and detaches nodes', () => {
const opacity = new Animated.Value(0);
const root = TestRenderer.create(<Animated.View style={{opacity}} />);
Animated.timing(opacity, {
toValue: 10,
duration: 1000,
useNativeDriver: true,
}).start();
expect(NativeAnimatedModule.createAnimatedNode).toHaveBeenCalledTimes(3);
expect(NativeAnimatedModule.connectAnimatedNodes).toHaveBeenCalledTimes(
2,
);
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
type: 'frames',
frames: expect.any(Array),
toValue: expect.any(Number),
iterations: 1,
},
expect.any(Function),
);
expect(
NativeAnimatedModule.disconnectAnimatedNodes,
).not.toHaveBeenCalled();
expect(NativeAnimatedModule.dropAnimatedNode).not.toHaveBeenCalled();
root.unmount();
expect(
NativeAnimatedModule.disconnectAnimatedNodes,
).toHaveBeenCalledTimes(2);
expect(NativeAnimatedModule.dropAnimatedNode).toHaveBeenCalledTimes(3);
});
it('sends a valid description for value, style and props nodes', () => {
const opacity = new Animated.Value(0);
TestRenderer.create(<Animated.View style={{opacity}} />);
Animated.timing(opacity, {
toValue: 10,
duration: 1000,
useNativeDriver: true,
}).start();
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'value', value: 0, offset: 0},
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'style', style: {opacity: expect.any(Number)}},
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'props', props: {style: expect.any(Number)}},
);
});
it('sends a valid graph description for Animated.add nodes', () => {
const first = new Animated.Value(1);
const second = new Animated.Value(2);
first.__makeNative();
second.__makeNative();
TestRenderer.create(
<Animated.View style={{opacity: Animated.add(first, second)}} />,
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'addition', input: expect.any(Array)},
);
const additionCalls =
NativeAnimatedModule.createAnimatedNode.mock.calls.filter(
call => call[1].type === 'addition',
);
expect(additionCalls.length).toBe(1);
const additionCall = additionCalls[0];
const additionNodeTag = additionCall[0];
const additionConnectionCalls =
NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
call => call[1] === additionNodeTag,
);
expect(additionConnectionCalls.length).toBe(2);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
additionCall[1].input[0],
{
type: 'value',
value: 1,
offset: 0,
},
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
additionCall[1].input[1],
{
type: 'value',
value: 2,
offset: 0,
},
);
});
it('sends a valid graph description for Animated.subtract nodes', () => {
const first = new Animated.Value(2);
const second = new Animated.Value(1);
first.__makeNative();
second.__makeNative();
TestRenderer.create(
<Animated.View style={{opacity: Animated.subtract(first, second)}} />,
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'subtraction', input: expect.any(Array)},
);
const subtractionCalls =
NativeAnimatedModule.createAnimatedNode.mock.calls.filter(
call => call[1].type === 'subtraction',
);
expect(subtractionCalls.length).toBe(1);
const subtractionCall = subtractionCalls[0];
const subtractionNodeTag = subtractionCall[0];
const subtractionConnectionCalls =
NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
call => call[1] === subtractionNodeTag,
);
expect(subtractionConnectionCalls.length).toBe(2);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
subtractionCall[1].input[0],
{
type: 'value',
value: 2,
offset: 0,
},
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
subtractionCall[1].input[1],
{
type: 'value',
value: 1,
offset: 0,
},
);
});
it('sends a valid graph description for Animated.multiply nodes', () => {
const first = new Animated.Value(2);
const second = new Animated.Value(1);
first.__makeNative();
second.__makeNative();
TestRenderer.create(
<Animated.View style={{opacity: Animated.multiply(first, second)}} />,
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'multiplication', input: expect.any(Array)},
);
const multiplicationCalls =
NativeAnimatedModule.createAnimatedNode.mock.calls.filter(
call => call[1].type === 'multiplication',
);
expect(multiplicationCalls.length).toBe(1);
const multiplicationCall = multiplicationCalls[0];
const multiplicationNodeTag = multiplicationCall[0];
const multiplicationConnectionCalls =
NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
call => call[1] === multiplicationNodeTag,
);
expect(multiplicationConnectionCalls.length).toBe(2);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
multiplicationCall[1].input[0],
{
type: 'value',
value: 2,
offset: 0,
},
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
multiplicationCall[1].input[1],
{
type: 'value',
value: 1,
offset: 0,
},
);
});
it('sends a valid graph description for Animated.divide nodes', () => {
const first = new Animated.Value(4);
const second = new Animated.Value(2);
first.__makeNative();
second.__makeNative();
TestRenderer.create(
<Animated.View style={{opacity: Animated.divide(first, second)}} />,
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'division', input: expect.any(Array)},
);
const divisionCalls =
NativeAnimatedModule.createAnimatedNode.mock.calls.filter(
call => call[1].type === 'division',
);
expect(divisionCalls.length).toBe(1);
const divisionCall = divisionCalls[0];
const divisionNodeTag = divisionCall[0];
const divisionConnectionCalls =
NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
call => call[1] === divisionNodeTag,
);
expect(divisionConnectionCalls.length).toBe(2);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
divisionCall[1].input[0],
{
type: 'value',
value: 4,
offset: 0,
},
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
divisionCall[1].input[1],
{
type: 'value',
value: 2,
offset: 0,
},
);
});
it('sends a valid graph description for Animated.modulo nodes', () => {
const value = new Animated.Value(4);
value.__makeNative();
TestRenderer.create(
<Animated.View style={{opacity: Animated.modulo(value, 4)}} />,
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'modulus', modulus: 4, input: expect.any(Number)},
);
const moduloCalls =
NativeAnimatedModule.createAnimatedNode.mock.calls.filter(
call => call[1].type === 'modulus',
);
expect(moduloCalls.length).toBe(1);
const moduloCall = moduloCalls[0];
const moduloNodeTag = moduloCall[0];
const moduloConnectionCalls =
NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
call => call[1] === moduloNodeTag,
);
expect(moduloConnectionCalls.length).toBe(1);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
moduloCall[1].input,
{
type: 'value',
value: 4,
offset: 0,
},
);
});
it('sends a valid graph description for interpolate() nodes', () => {
const value = new Animated.Value(10);
value.__makeNative();
TestRenderer.create(
<Animated.View
style={{
opacity: value.interpolate({
inputRange: [10, 20],
outputRange: [0, 1],
}),
}}
/>,
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'value', value: 10, offset: 0},
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{
type: 'interpolation',
inputRange: [10, 20],
outputRange: [0, 1],
extrapolateLeft: 'extend',
extrapolateRight: 'extend',
},
);
const interpolationNodeTag =
NativeAnimatedModule.createAnimatedNode.mock.calls.find(
call => call[1].type === 'interpolation',
)[0];
const valueNodeTag =
NativeAnimatedModule.createAnimatedNode.mock.calls.find(
call => call[1].type === 'value',
)[0];
expect(NativeAnimatedModule.connectAnimatedNodes).toBeCalledWith(
valueNodeTag,
interpolationNodeTag,
);
});
it('sends a valid graph description for transform nodes', () => {
const translateX = new Animated.Value(0);
translateX.__makeNative();
TestRenderer.create(
<Animated.View style={{transform: [{translateX}, {scale: 2}]}} />,
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{
type: 'transform',
transforms: [
{
nodeTag: expect.any(Number),
property: 'translateX',
type: 'animated',
},
{
value: 2,
property: 'scale',
type: 'static',
},
],
},
);
});
it('sends a valid graph description for Animated.diffClamp nodes', () => {
const value = new Animated.Value(2);
value.__makeNative();
TestRenderer.create(
<Animated.View style={{opacity: Animated.diffClamp(value, 0, 20)}} />,
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'diffclamp', input: expect.any(Number), max: 20, min: 0},
);
const diffClampCalls =
NativeAnimatedModule.createAnimatedNode.mock.calls.filter(
call => call[1].type === 'diffclamp',
);
expect(diffClampCalls.length).toBe(1);
const diffClampCall = diffClampCalls[0];
const diffClampNodeTag = diffClampCall[0];
const diffClampConnectionCalls =
NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
call => call[1] === diffClampNodeTag,
);
expect(diffClampConnectionCalls.length).toBe(1);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
diffClampCall[1].input,
{
type: 'value',
value: 2,
offset: 0,
},
);
});
it("doesn't call into native API if useNativeDriver is set to false", () => {
const opacity = new Animated.Value(0);
const root = TestRenderer.create(<Animated.View style={{opacity}} />);
Animated.timing(opacity, {
toValue: 10,
duration: 1000,
useNativeDriver: false,
}).start();
root.unmount();
expect(NativeAnimatedModule.createAnimatedNode).not.toBeCalled();
});
it('fails when trying to run non-native animation on native node', () => {
const opacity = new Animated.Value(0);
const ref = React.createRef(null);
TestRenderer.create(<Animated.View ref={ref} style={{opacity}} />);
// Necessary to simulate the native animation.
expect(ref.current).not.toBeNull();
ref.current.setNativeProps = jest.fn();
Animated.timing(opacity, {
toValue: 10,
duration: 50,
useNativeDriver: true,
}).start();
jest.runAllTimers();
Animated.timing(opacity, {
toValue: 4,
duration: 500,
useNativeDriver: false,
}).start();
try {
process.env.NODE_ENV = 'development';
expect(jest.runAllTimers).toThrow(
'Attempting to run JS driven animation on animated node that has ' +
'been moved to "native" earlier by starting an animation with ' +
'`useNativeDriver: true`',
);
} finally {
process.env.NODE_ENV = 'test';
}
});
it('fails for unsupported styles', () => {
const left = new Animated.Value(0);
TestRenderer.create(<Animated.View style={{left}} />);
const animation = Animated.timing(left, {
toValue: 10,
duration: 50,
useNativeDriver: true,
});
expect(animation.start).toThrowError(/left/);
});
it('works for any `static` props and styles', () => {
// Passing "unsupported" props should work just fine as long as they are not animated
const opacity = new Animated.Value(0);
opacity.__makeNative();
TestRenderer.create(
<Animated.View
removeClippedSubviews={true}
style={{left: 10, opacity, top: 20}}
/>,
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'style', style: {opacity: expect.any(Number)}},
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'props', props: {style: expect.any(Number)}},
);
});
});
describe('Animations', () => {
it('sends a valid timing animation description', () => {
const anim = new Animated.Value(0);
Animated.timing(anim, {
toValue: 10,
duration: 1000,
useNativeDriver: true,
}).start();
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
type: 'frames',
frames: expect.any(Array),
toValue: expect.any(Number),
iterations: 1,
},
expect.any(Function),
);
});
it('sends a valid spring animation description', () => {
const anim = new Animated.Value(0);
Animated.spring(anim, {
toValue: 10,
friction: 5,
tension: 164,
useNativeDriver: true,
}).start();
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
type: 'spring',
stiffness: 679.08,
damping: 16,
mass: 1,
initialVelocity: 0,
overshootClamping: false,
restDisplacementThreshold: 0.001,
restSpeedThreshold: 0.001,
toValue: 10,
iterations: 1,
},
expect.any(Function),
);
Animated.spring(anim, {
toValue: 10,
stiffness: 1000,
damping: 500,
mass: 3,
useNativeDriver: true,
}).start();
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
type: 'spring',
stiffness: 1000,
damping: 500,
mass: 3,
initialVelocity: 0,
overshootClamping: false,
restDisplacementThreshold: 0.001,
restSpeedThreshold: 0.001,
toValue: 10,
iterations: 1,
},
expect.any(Function),
);
Animated.spring(anim, {
toValue: 10,
bounciness: 8,
speed: 10,
useNativeDriver: true,
}).start();
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
type: 'spring',
damping: 23.05223140901191,
initialVelocity: 0,
overshootClamping: false,
restDisplacementThreshold: 0.001,
restSpeedThreshold: 0.001,
stiffness: 299.61882352941177,
mass: 1,
toValue: 10,
iterations: 1,
},
expect.any(Function),
);
});
it('sends a valid decay animation description', () => {
const anim = new Animated.Value(0);
Animated.decay(anim, {
velocity: 10,
deceleration: 0.1,
useNativeDriver: true,
}).start();
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{type: 'decay', deceleration: 0.1, velocity: 10, iterations: 1},
expect.any(Function),
);
});
it('works with Animated.loop', () => {
const anim = new Animated.Value(0);
Animated.loop(
Animated.decay(anim, {
velocity: 10,
deceleration: 0.1,
useNativeDriver: true,
}),
{iterations: 10},
).start();
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{type: 'decay', deceleration: 0.1, velocity: 10, iterations: 10},
expect.any(Function),
);
});
it('sends stopAnimation command to native', () => {
const value = new Animated.Value(0);
const animation = Animated.timing(value, {
toValue: 10,
duration: 50,
useNativeDriver: true,
});
animation.start();
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
type: 'frames',
frames: expect.any(Array),
toValue: expect.any(Number),
iterations: 1,
},
expect.any(Function),
);
const animationId =
NativeAnimatedModule.startAnimatingNode.mock.calls[0][0];
animation.stop();
expect(NativeAnimatedModule.stopAnimation).toBeCalledWith(animationId);
});
it('calls stopAnimation callback with native value', () => {
NativeAnimatedModule.getValue = jest.fn((tag, saveCallback) => {
saveCallback(1);
});
const anim = new Animated.Value(0);
Animated.timing(anim, {
duration: 1000,
useNativeDriver: true,
}).start();
const tag = anim.__getNativeTag();
let currentValue = 0;
anim.stopAnimation(value => (currentValue = value));
expect(NativeAnimatedModule.getValue).toBeCalledWith(
tag,
expect.any(Function),
);
expect(currentValue).toEqual(1);
});
});
describe('Animated Components', () => {
it('Should restore default values on prop updates only', () => {
const opacity = new Animated.Value(0);
opacity.__makeNative();
const root = TestRenderer.create(<Animated.View style={{opacity}} />);
expect(NativeAnimatedModule.restoreDefaultValues).not.toHaveBeenCalled();
root.update(<Animated.View style={{opacity}} />);
expect(NativeAnimatedModule.restoreDefaultValues).toHaveBeenCalledWith(
expect.any(Number),
);
root.unmount();
// Make sure it doesn't get called on unmount.
expect(NativeAnimatedModule.restoreDefaultValues).toHaveBeenCalledTimes(
1,
);
});
});
});
@@ -5,12 +5,12 @@
* LICENSE file in the root directory of this source tree.
*
* @format
* @oncall react_native
* @emails oncall+react_native
*/
'use strict';
import Easing from '../Easing';
const Easing = require('../Easing');
describe('Easing', () => {
it('should work with linear', () => {
const easing = Easing.linear;
@@ -0,0 +1,316 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @emails oncall+react_native
*/
'use strict';
const AnimatedInterpolation = require('../nodes/AnimatedInterpolation');
const Easing = require('../Easing');
describe('Interpolation', () => {
it('should work with defaults', () => {
const interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1],
outputRange: [0, 1],
});
expect(interpolation(0)).toBe(0);
expect(interpolation(0.5)).toBe(0.5);
expect(interpolation(0.8)).toBe(0.8);
expect(interpolation(1)).toBe(1);
});
it('should work with output range', () => {
const interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1],
outputRange: [100, 200],
});
expect(interpolation(0)).toBe(100);
expect(interpolation(0.5)).toBe(150);
expect(interpolation(0.8)).toBe(180);
expect(interpolation(1)).toBe(200);
});
it('should work with input range', () => {
const interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [100, 200],
outputRange: [0, 1],
});
expect(interpolation(100)).toBe(0);
expect(interpolation(150)).toBe(0.5);
expect(interpolation(180)).toBe(0.8);
expect(interpolation(200)).toBe(1);
});
it('should throw for non monotonic input ranges', () => {
expect(() =>
AnimatedInterpolation.__createInterpolation({
inputRange: [0, 2, 1],
outputRange: [0, 1, 2],
}),
).toThrow();
expect(() =>
AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1, 2],
outputRange: [0, 3, 1],
}),
).not.toThrow();
});
it('should work with empty input range', () => {
const interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [0, 10, 10],
outputRange: [1, 2, 3],
extrapolate: 'extend',
});
expect(interpolation(0)).toBe(1);
expect(interpolation(5)).toBe(1.5);
expect(interpolation(10)).toBe(2);
expect(interpolation(10.1)).toBe(3);
expect(interpolation(15)).toBe(3);
});
it('should work with empty output range', () => {
const interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [1, 2, 3],
outputRange: [0, 10, 10],
extrapolate: 'extend',
});
expect(interpolation(0)).toBe(-10);
expect(interpolation(1.5)).toBe(5);
expect(interpolation(2)).toBe(10);
expect(interpolation(2.5)).toBe(10);
expect(interpolation(3)).toBe(10);
expect(interpolation(4)).toBe(10);
});
it('should work with easing', () => {
const interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1],
outputRange: [0, 1],
easing: Easing.quad,
});
expect(interpolation(0)).toBe(0);
expect(interpolation(0.5)).toBe(0.25);
expect(interpolation(0.9)).toBe(0.81);
expect(interpolation(1)).toBe(1);
});
it('should work with extrapolate', () => {
let interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolate: 'extend',
easing: Easing.quad,
});
expect(interpolation(-2)).toBe(4);
expect(interpolation(2)).toBe(4);
interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolate: 'clamp',
easing: Easing.quad,
});
expect(interpolation(-2)).toBe(0);
expect(interpolation(2)).toBe(1);
interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolate: 'identity',
easing: Easing.quad,
});
expect(interpolation(-2)).toBe(-2);
expect(interpolation(2)).toBe(2);
});
it('should work with keyframes with extrapolate', () => {
const interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [0, 10, 100, 1000],
outputRange: [0, 5, 50, 500],
extrapolate: true,
});
expect(interpolation(-5)).toBe(-2.5);
expect(interpolation(0)).toBe(0);
expect(interpolation(5)).toBe(2.5);
expect(interpolation(10)).toBe(5);
expect(interpolation(50)).toBe(25);
expect(interpolation(100)).toBe(50);
expect(interpolation(500)).toBe(250);
expect(interpolation(1000)).toBe(500);
expect(interpolation(2000)).toBe(1000);
});
it('should work with keyframes without extrapolate', () => {
const interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1, 2],
outputRange: [0.2, 1, 0.2],
extrapolate: 'clamp',
});
expect(interpolation(5)).toBeCloseTo(0.2);
});
it('should throw for an infinite input range', () => {
expect(() =>
AnimatedInterpolation.__createInterpolation({
inputRange: [-Infinity, Infinity],
outputRange: [0, 1],
}),
).toThrow();
expect(() =>
AnimatedInterpolation.__createInterpolation({
inputRange: [-Infinity, 0, Infinity],
outputRange: [1, 2, 3],
}),
).not.toThrow();
});
it('should work with negative infinite', () => {
const interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [-Infinity, 0],
outputRange: [-Infinity, 0],
easing: Easing.quad,
extrapolate: 'identity',
});
expect(interpolation(-Infinity)).toBe(-Infinity);
expect(interpolation(-100)).toBeCloseTo(-10000);
expect(interpolation(-10)).toBeCloseTo(-100);
expect(interpolation(0)).toBeCloseTo(0);
expect(interpolation(1)).toBeCloseTo(1);
expect(interpolation(100)).toBeCloseTo(100);
});
it('should work with positive infinite', () => {
const interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [5, Infinity],
outputRange: [5, Infinity],
easing: Easing.quad,
extrapolate: 'identity',
});
expect(interpolation(-100)).toBeCloseTo(-100);
expect(interpolation(-10)).toBeCloseTo(-10);
expect(interpolation(0)).toBeCloseTo(0);
expect(interpolation(5)).toBeCloseTo(5);
expect(interpolation(6)).toBeCloseTo(5 + 1);
expect(interpolation(10)).toBeCloseTo(5 + 25);
expect(interpolation(100)).toBeCloseTo(5 + 95 * 95);
expect(interpolation(Infinity)).toBe(Infinity);
});
it('should work with output ranges as string', () => {
const interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1],
outputRange: ['rgba(0, 100, 200, 0)', 'rgba(50, 150, 250, 0.4)'],
});
expect(interpolation(0)).toBe('rgba(0, 100, 200, 0)');
expect(interpolation(0.5)).toBe('rgba(25, 125, 225, 0.2)');
expect(interpolation(1)).toBe('rgba(50, 150, 250, 0.4)');
});
it('should work with output ranges as short hex string', () => {
const interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1],
outputRange: ['#024', '#9BF'],
});
expect(interpolation(0)).toBe('rgba(0, 34, 68, 1)');
expect(interpolation(0.5)).toBe('rgba(77, 111, 162, 1)');
expect(interpolation(1)).toBe('rgba(153, 187, 255, 1)');
});
it('should work with output ranges as long hex string', () => {
const interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1],
outputRange: ['#FF9500', '#87FC70'],
});
expect(interpolation(0)).toBe('rgba(255, 149, 0, 1)');
expect(interpolation(0.5)).toBe('rgba(195, 201, 56, 1)');
expect(interpolation(1)).toBe('rgba(135, 252, 112, 1)');
});
it('should work with output ranges with mixed hex and rgba strings', () => {
const interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1],
outputRange: ['rgba(100, 120, 140, .4)', '#87FC70'],
});
expect(interpolation(0)).toBe('rgba(100, 120, 140, 0.4)');
expect(interpolation(0.5)).toBe('rgba(118, 186, 126, 0.7)');
expect(interpolation(1)).toBe('rgba(135, 252, 112, 1)');
});
it('should work with negative and decimal values in string ranges', () => {
const interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1],
outputRange: ['-100.5deg', '100deg'],
});
expect(interpolation(0)).toBe('-100.5deg');
expect(interpolation(0.5)).toBe('-0.25deg');
expect(interpolation(1)).toBe('100deg');
});
it('should crash when chaining an interpolation that returns a string', () => {
const interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1],
outputRange: [0, 1],
});
expect(() => {
interpolation('45rad');
}).toThrow();
});
it('should support a mix of color patterns', () => {
const interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1, 2],
outputRange: ['rgba(0, 100, 200, 0)', 'rgb(50, 150, 250)', 'red'],
});
expect(interpolation(0)).toBe('rgba(0, 100, 200, 0)');
expect(interpolation(0.5)).toBe('rgba(25, 125, 225, 0.5)');
expect(interpolation(1.5)).toBe('rgba(153, 75, 125, 1)');
expect(interpolation(2)).toBe('rgba(255, 0, 0, 1)');
});
it('should crash when defining output range with different pattern', () => {
expect(() =>
AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1],
outputRange: ['20deg', '30rad'],
}),
).toThrow();
});
it('should round the alpha channel of a color to the nearest thousandth', () => {
const interpolation = AnimatedInterpolation.__createInterpolation({
inputRange: [0, 1],
outputRange: ['rgba(0, 0, 0, 0)', 'rgba(0, 0, 0, 1)'],
});
expect(interpolation(1e-12)).toBe('rgba(0, 0, 0, 0)');
expect(interpolation(2 / 3)).toBe('rgba(0, 0, 0, 0.667)');
});
});
@@ -5,12 +5,12 @@
* LICENSE file in the root directory of this source tree.
*
* @format
* @oncall react_native
* @emails oncall+react_native
*/
'use strict';
import TimingAnimation from '../animations/TimingAnimation';
const TimingAnimation = require('../animations/TimingAnimation');
describe('Timing Animation', () => {
it('should return array of 61 items', () => {
@@ -4,9 +4,9 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails oncall+react_native
* @flow
* @format
* @oncall react_native
*/
/**
@@ -17,28 +17,23 @@
'use strict';
import bezier from '../bezier';
const bezier = require('../bezier');
const identity = function (x: number) {
const identity = function (x) {
return x;
};
function assertClose(a: number, b: number, precision: number = 3) {
function assertClose(a, b, precision = 3) {
expect(a).toBeCloseTo(b, precision);
}
function makeAssertCloseWithPrecision(precision: number) {
return function (a: number, b: number) {
function makeAssertCloseWithPrecision(precision) {
return function (a, b) {
assertClose(a, b, precision);
};
}
function allEquals(
be1: (x: number) => number,
be2: (x: number) => number,
samples: number,
assertion: $FlowFixMe,
) {
function allEquals(be1, be2, samples, assertion) {
if (!assertion) {
assertion = assertClose;
}
@@ -48,8 +43,8 @@ function allEquals(
}
}
function repeat(n: number) {
return function (f: () => void) {
function repeat(n) {
return function (f) {
for (let i = 0; i < n; ++i) {
f();
}
@@ -104,7 +99,7 @@ describe('bezier', function () {
d = Math.random();
const easing = bezier(a, b, c, d);
const projected = bezier(b, a, d, c);
const composed = function (x: number) {
const composed = function (x) {
return projected(easing(x));
};
allEquals(identity, composed, 100, makeAssertCloseWithPrecision(2));
@@ -140,7 +135,7 @@ describe('bezier', function () {
c = 1 - a,
d = 1 - b;
const easing = bezier(a, b, c, d);
const sym = function (x: number) {
const sym = function (x) {
return 1 - easing(1 - x);
};
allEquals(easing, sym, 100, makeAssertCloseWithPrecision(2));
@@ -0,0 +1,67 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails oncall+react_native
* @flow strict-local
* @format
*/
'use strict';
const createAnimatedComponent = require('../createAnimatedComponent');
const createAnimatedComponentInjection = require('../createAnimatedComponentInjection');
const React = require('react');
function injected<TProps: {...}, TInstance>(
Component: React.AbstractComponent<TProps, TInstance>,
): React.AbstractComponent<TProps, TInstance> {
return createAnimatedComponent(Component);
}
beforeEach(() => {
jest.resetModules();
jest.resetAllMocks();
});
test('does nothing without injection', () => {
expect(typeof createAnimatedComponent).toBe('function');
expect(createAnimatedComponent).not.toBe(injected);
});
test('injection overrides `createAnimatedComponent`', () => {
createAnimatedComponentInjection.inject(injected);
expect(createAnimatedComponent).toBe(injected);
});
test('injection errors if called too late', () => {
jest.spyOn(console, 'error').mockReturnValue(undefined);
// Causes `createAnimatedComponent` to be initialized.
createAnimatedComponent;
createAnimatedComponentInjection.inject(injected);
expect(createAnimatedComponent).not.toBe(injected);
expect(console.error).toBeCalledWith(
'createAnimatedComponentInjection: Must be called before `createAnimatedComponent`.',
);
});
test('injection errors if called more than once', () => {
jest.spyOn(console, 'error').mockReturnValue(undefined);
createAnimatedComponentInjection.inject(injected);
expect(createAnimatedComponent).toBe(injected);
expect(console.error).not.toBeCalled();
createAnimatedComponentInjection.inject(injected);
expect(console.error).toBeCalledWith(
'createAnimatedComponentInjection: Cannot be called more than once.',
);
});
@@ -10,11 +10,10 @@
'use strict';
const NativeAnimatedHelper = require('../NativeAnimatedHelper');
import type {PlatformConfig} from '../AnimatedPlatformConfig';
import type AnimatedValue from '../nodes/AnimatedValue';
import NativeAnimatedHelper from '../NativeAnimatedHelper';
export type EndResult = {finished: boolean, ...};
export type EndCallback = (result: EndResult) => void;
@@ -31,7 +30,7 @@ let startNativeAnimationNextId = 1;
// Important note: start() and stop() will only be called at most once.
// Once an animation has been stopped or finished its course, it will
// not be reused.
export default class Animation {
class Animation {
__active: boolean;
__isInteraction: boolean;
__nativeId: number;
@@ -86,3 +85,5 @@ export default class Animation {
}
}
}
module.exports = Animation;
@@ -10,13 +10,14 @@
'use strict';
const Animation = require('./Animation');
const {shouldUseNativeDriver} = require('../NativeAnimatedHelper');
import type {PlatformConfig} from '../AnimatedPlatformConfig';
import type AnimatedValue from '../nodes/AnimatedValue';
import type {AnimationConfig, EndCallback} from './Animation';
import NativeAnimatedHelper from '../NativeAnimatedHelper';
import Animation from './Animation';
export type DecayAnimationConfig = {
...AnimationConfig,
velocity:
@@ -35,7 +36,7 @@ export type DecayAnimationConfigSingle = {
deceleration?: number,
};
export default class DecayAnimation extends Animation {
class DecayAnimation extends Animation {
_startTime: number;
_lastValue: number;
_fromValue: number;
@@ -50,7 +51,7 @@ export default class DecayAnimation extends Animation {
super();
this._deceleration = config.deceleration ?? 0.998;
this._velocity = config.velocity;
this._useNativeDriver = NativeAnimatedHelper.shouldUseNativeDriver(config);
this._useNativeDriver = shouldUseNativeDriver(config);
this._platformConfig = config.platformConfig;
this.__isInteraction = config.isInteraction ?? !this._useNativeDriver;
this.__iterations = config.iterations ?? 1;
@@ -122,3 +123,5 @@ export default class DecayAnimation extends Animation {
this.__debouncedOnEnd({finished: false});
}
}
module.exports = DecayAnimation;
@@ -10,17 +10,20 @@
'use strict';
const AnimatedValue = require('../nodes/AnimatedValue');
const AnimatedValueXY = require('../nodes/AnimatedValueXY');
const AnimatedInterpolation = require('../nodes/AnimatedInterpolation');
const Animation = require('./Animation');
const SpringConfig = require('../SpringConfig');
const invariant = require('invariant');
const {shouldUseNativeDriver} = require('../NativeAnimatedHelper');
import type {PlatformConfig} from '../AnimatedPlatformConfig';
import type AnimatedInterpolation from '../nodes/AnimatedInterpolation';
import type AnimatedValue from '../nodes/AnimatedValue';
import type AnimatedValueXY from '../nodes/AnimatedValueXY';
import type {AnimationConfig, EndCallback} from './Animation';
import NativeAnimatedHelper from '../NativeAnimatedHelper';
import AnimatedColor from '../nodes/AnimatedColor';
import * as SpringConfig from '../SpringConfig';
import Animation from './Animation';
import invariant from 'invariant';
export type SpringAnimationConfig = {
...AnimationConfig,
@@ -41,7 +44,7 @@ export type SpringAnimationConfig = {
...
}
| AnimatedColor
| AnimatedInterpolation<number>,
| AnimatedInterpolation,
overshootClamping?: boolean,
restDisplacementThreshold?: number,
restSpeedThreshold?: number,
@@ -64,7 +67,7 @@ export type SpringAnimationConfig = {
export type SpringAnimationConfigSingle = {
...AnimationConfig,
toValue: number,
toValue: number | AnimatedValue | AnimatedInterpolation,
overshootClamping?: boolean,
restDisplacementThreshold?: number,
restSpeedThreshold?: number,
@@ -79,7 +82,7 @@ export type SpringAnimationConfigSingle = {
delay?: number,
};
export default class SpringAnimation extends Animation {
class SpringAnimation extends Animation {
_overshootClamping: boolean;
_restDisplacementThreshold: number;
_restSpeedThreshold: number;
@@ -87,7 +90,7 @@ export default class SpringAnimation extends Animation {
_startPosition: number;
_lastPosition: number;
_fromValue: number;
_toValue: number;
_toValue: any;
_stiffness: number;
_damping: number;
_mass: number;
@@ -112,7 +115,7 @@ export default class SpringAnimation extends Animation {
this._lastVelocity = config.velocity ?? 0;
this._toValue = config.toValue;
this._delay = config.delay ?? 0;
this._useNativeDriver = NativeAnimatedHelper.shouldUseNativeDriver(config);
this._useNativeDriver = shouldUseNativeDriver(config);
this._platformConfig = config.platformConfig;
this.__isInteraction = config.isInteraction ?? !this._useNativeDriver;
this.__iterations = config.iterations ?? 1;
@@ -368,3 +371,5 @@ export default class SpringAnimation extends Animation {
this.__debouncedOnEnd({finished: false});
}
}
module.exports = SpringAnimation;
@@ -10,18 +10,19 @@
'use strict';
const AnimatedValue = require('../nodes/AnimatedValue');
const AnimatedValueXY = require('../nodes/AnimatedValueXY');
const AnimatedInterpolation = require('../nodes/AnimatedInterpolation');
const Animation = require('./Animation');
const {shouldUseNativeDriver} = require('../NativeAnimatedHelper');
import type {PlatformConfig} from '../AnimatedPlatformConfig';
import type {RgbaValue} from '../nodes/AnimatedColor';
import type AnimatedInterpolation from '../nodes/AnimatedInterpolation';
import type AnimatedValue from '../nodes/AnimatedValue';
import type AnimatedValueXY from '../nodes/AnimatedValueXY';
import type {AnimationConfig, EndCallback} from './Animation';
import NativeAnimatedHelper from '../NativeAnimatedHelper';
import AnimatedColor from '../nodes/AnimatedColor';
import Animation from './Animation';
export type TimingAnimationConfig = $ReadOnly<{
export type TimingAnimationConfig = {
...AnimationConfig,
toValue:
| number
@@ -32,35 +33,41 @@ export type TimingAnimationConfig = $ReadOnly<{
...
}
| AnimatedValueXY
| RgbaValue
| {
r: number,
g: number,
b: number,
a: number,
...
}
| AnimatedColor
| AnimatedInterpolation<number>,
| AnimatedInterpolation,
easing?: (value: number) => number,
duration?: number,
delay?: number,
}>;
};
export type TimingAnimationConfigSingle = $ReadOnly<{
export type TimingAnimationConfigSingle = {
...AnimationConfig,
toValue: number,
toValue: number | AnimatedValue | AnimatedInterpolation,
easing?: (value: number) => number,
duration?: number,
delay?: number,
}>;
};
let _easeInOut;
function easeInOut() {
if (!_easeInOut) {
const Easing = require('../Easing').default;
const Easing = require('../Easing');
_easeInOut = Easing.inOut(Easing.ease);
}
return _easeInOut;
}
export default class TimingAnimation extends Animation {
class TimingAnimation extends Animation {
_startTime: number;
_fromValue: number;
_toValue: number;
_toValue: any;
_duration: number;
_delay: number;
_easing: (value: number) => number;
@@ -77,7 +84,7 @@ export default class TimingAnimation extends Animation {
this._duration = config.duration ?? 500;
this._delay = config.delay ?? 0;
this.__iterations = config.iterations ?? 1;
this._useNativeDriver = NativeAnimatedHelper.shouldUseNativeDriver(config);
this._useNativeDriver = shouldUseNativeDriver(config);
this._platformConfig = config.platformConfig;
this.__isInteraction = config.isInteraction ?? !this._useNativeDriver;
}
@@ -170,3 +177,5 @@ export default class TimingAnimation extends Animation {
this.__debouncedOnEnd({finished: false});
}
}
module.exports = TimingAnimation;
@@ -92,7 +92,7 @@ function newtonRaphsonIterate(
return aGuessT;
}
export default function bezier(
module.exports = function bezier(
mX1: number,
mY1: number,
mX2: number,
@@ -105,7 +105,7 @@ export default function bezier(
// Precompute samples table
const sampleValues = float32ArraySupported
? new Float32Array(kSplineTableSize)
: new Array<number>(kSplineTableSize);
: new Array(kSplineTableSize);
if (mX1 !== mY1 || mX2 !== mY2) {
for (let i = 0; i < kSplineTableSize; ++i) {
sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
@@ -161,4 +161,4 @@ export default function bezier(
}
return calcBezier(getTForX(x), mY1, mY2);
};
}
};
@@ -0,0 +1,30 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import * as React from 'react';
const FlatList = require('../../Lists/FlatList');
const createAnimatedComponent = require('../createAnimatedComponent');
import type {AnimatedComponentType} from '../createAnimatedComponent';
/**
* @see https://github.com/facebook/react-native/commit/b8c8562
*/
const FlatListWithEventThrottle = React.forwardRef((props, ref) => (
<FlatList scrollEventThrottle={0.0001} {...props} ref={ref} />
));
module.exports = (createAnimatedComponent(
FlatListWithEventThrottle,
): AnimatedComponentType<
React.ElementConfig<typeof FlatList>,
React.ElementRef<typeof FlatList>,
>);
@@ -0,0 +1,23 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import * as React from 'react';
const Image = require('../../Image/Image');
const createAnimatedComponent = require('../createAnimatedComponent');
import type {AnimatedComponentType} from '../createAnimatedComponent';
module.exports = (createAnimatedComponent(
(Image: $FlowFixMe),
): AnimatedComponentType<
React.ElementConfig<typeof Image>,
React.ElementRef<typeof Image>,
>);
@@ -0,0 +1,30 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import * as React from 'react';
const ScrollView = require('../../Components/ScrollView/ScrollView');
const createAnimatedComponent = require('../createAnimatedComponent');
import type {AnimatedComponentType} from '../createAnimatedComponent';
/**
* @see https://github.com/facebook/react-native/commit/b8c8562
*/
const ScrollViewWithEventThrottle = React.forwardRef((props, ref) => (
<ScrollView scrollEventThrottle={0.0001} {...props} ref={ref} />
));
module.exports = (createAnimatedComponent(
ScrollViewWithEventThrottle,
): AnimatedComponentType<
React.ElementConfig<typeof ScrollView>,
React.ElementRef<typeof ScrollView>,
>);
@@ -0,0 +1,30 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import * as React from 'react';
import SectionList from '../../Lists/SectionList';
const createAnimatedComponent = require('../createAnimatedComponent');
import type {AnimatedComponentType} from '../createAnimatedComponent';
/**
* @see https://github.com/facebook/react-native/commit/b8c8562
*/
const SectionListWithEventThrottle = React.forwardRef((props, ref) => (
<SectionList scrollEventThrottle={0.0001} {...props} ref={ref} />
));
module.exports = (createAnimatedComponent(
SectionListWithEventThrottle,
): AnimatedComponentType<
React.ElementConfig<typeof SectionList>,
React.ElementRef<typeof SectionList>,
>);
@@ -0,0 +1,23 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import * as React from 'react';
const Text = require('../../Text/Text');
const createAnimatedComponent = require('../createAnimatedComponent');
import type {AnimatedComponentType} from '../createAnimatedComponent';
module.exports = (createAnimatedComponent(
(Text: $FlowFixMe),
): AnimatedComponentType<
React.ElementConfig<typeof Text>,
React.ElementRef<typeof Text>,
>);
@@ -0,0 +1,21 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import * as React from 'react';
const View = require('../../Components/View/View');
const createAnimatedComponent = require('../createAnimatedComponent');
import type {AnimatedComponentType} from '../createAnimatedComponent';
module.exports = (createAnimatedComponent(View): AnimatedComponentType<
React.ElementConfig<typeof View>,
React.ElementRef<typeof View>,
>);
@@ -0,0 +1,274 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
import * as createAnimatedComponentInjection from './createAnimatedComponentInjection';
const View = require('../Components/View/View');
const {AnimatedEvent} = require('./AnimatedEvent');
const AnimatedProps = require('./nodes/AnimatedProps');
const React = require('react');
const NativeAnimatedHelper = require('./NativeAnimatedHelper');
const invariant = require('invariant');
const setAndForwardRef = require('../Utilities/setAndForwardRef');
let animatedComponentNextId = 1;
export type AnimatedComponentType<
Props: {+[string]: mixed, ...},
Instance,
> = React.AbstractComponent<
$ObjMap<
Props &
$ReadOnly<{
passthroughAnimatedPropExplicitValues?: React.ElementConfig<
typeof View,
>,
}>,
() => any,
>,
Instance,
>;
function createAnimatedComponent<Props: {+[string]: mixed, ...}, Instance>(
Component: React.AbstractComponent<Props, Instance>,
): AnimatedComponentType<Props, Instance> {
invariant(
typeof Component !== 'function' ||
(Component.prototype && Component.prototype.isReactComponent),
'`createAnimatedComponent` does not support stateless functional components; ' +
'use a class component instead.',
);
class AnimatedComponent extends React.Component<Object> {
_component: any; // TODO T53738161: flow type this, and the whole file
_invokeAnimatedPropsCallbackOnMount: boolean = false;
_prevComponent: any;
_propsAnimated: AnimatedProps;
_eventDetachers: Array<Function> = [];
// Only to be used in this file, and only in Fabric.
_animatedComponentId: string = `${animatedComponentNextId++}:animatedComponent`;
_attachNativeEvents() {
// Make sure to get the scrollable node for components that implement
// `ScrollResponder.Mixin`.
const scrollableNode = this._component?.getScrollableNode
? this._component.getScrollableNode()
: this._component;
for (const key in this.props) {
const prop = this.props[key];
if (prop instanceof AnimatedEvent && prop.__isNative) {
prop.__attach(scrollableNode, key);
this._eventDetachers.push(() => prop.__detach(scrollableNode, key));
}
}
}
_detachNativeEvents() {
this._eventDetachers.forEach(remove => remove());
this._eventDetachers = [];
}
_isFabric = (): boolean => {
// When called during the first render, `_component` is always null.
// Therefore, even if a component is rendered in Fabric, we can't detect
// that until ref is set, which happens sometime after the first render.
// In cases where this value switching between "false" and "true" on Fabric
// causes issues, add an additional check for _component nullity.
if (this._component == null) {
return false;
}
return (
// eslint-disable-next-line dot-notation
this._component['_internalInstanceHandle']?.stateNode?.canonical !=
null ||
// Some components have a setNativeProps function but aren't a host component
// such as lists like FlatList and SectionList. These should also use
// forceUpdate in Fabric since setNativeProps doesn't exist on the underlying
// host component. This crazy hack is essentially special casing those lists and
// ScrollView itself to use forceUpdate in Fabric.
// If these components end up using forwardRef then these hacks can go away
// as this._component would actually be the underlying host component and the above check
// would be sufficient.
(this._component.getNativeScrollRef != null &&
this._component.getNativeScrollRef() != null &&
// eslint-disable-next-line dot-notation
this._component.getNativeScrollRef()['_internalInstanceHandle']
?.stateNode?.canonical != null) ||
(this._component.getScrollResponder != null &&
this._component.getScrollResponder() != null &&
this._component.getScrollResponder().getNativeScrollRef != null &&
this._component.getScrollResponder().getNativeScrollRef() != null &&
this._component.getScrollResponder().getNativeScrollRef()[
// eslint-disable-next-line dot-notation
'_internalInstanceHandle'
]?.stateNode?.canonical != null)
);
};
_waitForUpdate = (): void => {
if (this._isFabric()) {
NativeAnimatedHelper.API.setWaitingForIdentifier(
this._animatedComponentId,
);
}
};
_markUpdateComplete = (): void => {
if (this._isFabric()) {
NativeAnimatedHelper.API.unsetWaitingForIdentifier(
this._animatedComponentId,
);
}
};
// The system is best designed when setNativeProps is implemented. It is
// able to avoid re-rendering and directly set the attributes that changed.
// However, setNativeProps can only be implemented on leaf native
// components. If you want to animate a composite component, you need to
// re-render it. In this case, we have a fallback that uses forceUpdate.
// This fallback is also called in Fabric.
_animatedPropsCallback = () => {
if (this._component == null) {
// AnimatedProps is created in will-mount because it's used in render.
// But this callback may be invoked before mount in async mode,
// In which case we should defer the setNativeProps() call.
// React may throw away uncommitted work in async mode,
// So a deferred call won't always be invoked.
this._invokeAnimatedPropsCallbackOnMount = true;
} else if (
process.env.NODE_ENV === 'test' ||
// For animating properties of non-leaf/non-native components
typeof this._component.setNativeProps !== 'function' ||
// In Fabric, force animations to go through forceUpdate and skip setNativeProps
this._isFabric()
) {
this.forceUpdate();
} else if (!this._propsAnimated.__isNative) {
this._component.setNativeProps(
this._propsAnimated.__getAnimatedValue(),
);
} else {
throw new Error(
'Attempting to run JS driven animation on animated ' +
'node that has been moved to "native" earlier by starting an ' +
'animation with `useNativeDriver: true`',
);
}
};
_attachProps(nextProps: any) {
const oldPropsAnimated = this._propsAnimated;
this._propsAnimated = new AnimatedProps(
nextProps,
this._animatedPropsCallback,
);
this._propsAnimated.__attach();
// When you call detach, it removes the element from the parent list
// of children. If it goes to 0, then the parent also detaches itself
// and so on.
// An optimization is to attach the new elements and THEN detach the old
// ones instead of detaching and THEN attaching.
// This way the intermediate state isn't to go to 0 and trigger
// this expensive recursive detaching to then re-attach everything on
// the very next operation.
if (oldPropsAnimated) {
oldPropsAnimated.__restoreDefaultValues();
oldPropsAnimated.__detach();
}
}
_setComponentRef = setAndForwardRef({
getForwardedRef: () => this.props.forwardedRef,
setLocalRef: ref => {
this._prevComponent = this._component;
this._component = ref;
},
});
render() {
const {style = {}, ...props} = this._propsAnimated.__getValue() || {};
const {style: passthruStyle = {}, ...passthruProps} =
this.props.passthroughAnimatedPropExplicitValues || {};
const mergedStyle = {...style, ...passthruStyle};
// Force `collapsable` to be false so that native view is not flattened.
// Flattened views cannot be accurately referenced by a native driver.
return (
<Component
{...props}
{...passthruProps}
collapsable={false}
style={mergedStyle}
ref={this._setComponentRef}
/>
);
}
UNSAFE_componentWillMount() {
this._waitForUpdate();
this._attachProps(this.props);
}
componentDidMount() {
if (this._invokeAnimatedPropsCallbackOnMount) {
this._invokeAnimatedPropsCallbackOnMount = false;
this._animatedPropsCallback();
}
this._propsAnimated.setNativeView(this._component);
this._attachNativeEvents();
this._markUpdateComplete();
}
UNSAFE_componentWillReceiveProps(newProps: any) {
this._waitForUpdate();
this._attachProps(newProps);
}
componentDidUpdate(prevProps: any) {
if (this._component !== this._prevComponent) {
this._propsAnimated.setNativeView(this._component);
}
if (this._component !== this._prevComponent || prevProps !== this.props) {
this._detachNativeEvents();
this._attachNativeEvents();
}
this._markUpdateComplete();
}
componentWillUnmount() {
this._propsAnimated && this._propsAnimated.__detach();
this._detachNativeEvents();
this._markUpdateComplete();
this._component = null;
this._prevComponent = null;
}
}
return React.forwardRef(function AnimatedComponentWrapper(props, ref) {
return (
<AnimatedComponent
{...props}
{...(ref == null ? null : {forwardedRef: ref})}
/>
);
});
}
// $FlowIgnore[incompatible-cast] - Will be compatible after refactors.
module.exports = (createAnimatedComponentInjection.recordAndRetrieve() ??
createAnimatedComponent: typeof createAnimatedComponent);
@@ -0,0 +1,48 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import * as React from 'react';
type createAnimatedComponent = <TProps: {...}, TInstance>(
Component: React.AbstractComponent<TProps, TInstance>,
) => React.AbstractComponent<TProps, TInstance>;
// This can be undefined, null, or the experimental implementation. If this is
// null, that means `createAnimatedComponent` has already been initialized and
// it is too late to call `inject`.
let injected: ?createAnimatedComponent;
/**
* Call during bundle initialization to opt-in to new `createAnimatedComponent`.
*/
export function inject(newInjected: createAnimatedComponent): void {
if (injected !== undefined) {
if (__DEV__) {
console.error(
'createAnimatedComponentInjection: ' +
(injected == null
? 'Must be called before `createAnimatedComponent`.'
: 'Cannot be called more than once.'),
);
}
return;
}
injected = newInjected;
}
/**
* Only called by `createAnimatedComponent.js`.
*/
export function recordAndRetrieve(): createAnimatedComponent | null {
if (injected === undefined) {
injected = null;
}
return injected;
}
@@ -0,0 +1,48 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import useAnimatedProps from './useAnimatedProps';
import useMergeRefs from '../Utilities/useMergeRefs';
import StyleSheet from '../StyleSheet/StyleSheet';
import * as React from 'react';
/**
* Experimental implementation of `createAnimatedComponent` that is intended to
* be compatible with concurrent rendering.
*/
export default function createAnimatedComponent<TProps: {...}, TInstance>(
Component: React.AbstractComponent<TProps, TInstance>,
): React.AbstractComponent<TProps, TInstance> {
return React.forwardRef((props, forwardedRef) => {
const [reducedProps, callbackRef] = useAnimatedProps<TProps, TInstance>(
props,
);
const ref = useMergeRefs<TInstance | null>(callbackRef, forwardedRef);
// Some components require explicit passthrough values for animation
// to work properly. For example, if an animated component is
// transformed and Pressable, onPress will not work after transform
// without these passthrough values.
// $FlowFixMe[prop-missing]
const {passthroughAnimatedPropExplicitValues, style} = reducedProps;
const {style: passthroughStyle, ...passthroughProps} =
passthroughAnimatedPropExplicitValues ?? {};
const mergedStyle = StyleSheet.compose(style, passthroughStyle);
return (
<Component
{...reducedProps}
{...passthroughProps}
style={mergedStyle}
ref={ref}
/>
);
});
}
@@ -10,15 +10,15 @@
'use strict';
const AnimatedInterpolation = require('./AnimatedInterpolation');
const AnimatedNode = require('./AnimatedNode');
const AnimatedValue = require('./AnimatedValue');
const AnimatedWithChildren = require('./AnimatedWithChildren');
import type {PlatformConfig} from '../AnimatedPlatformConfig';
import type {InterpolationConfigType} from './AnimatedInterpolation';
import type AnimatedNode from './AnimatedNode';
import AnimatedInterpolation from './AnimatedInterpolation';
import AnimatedValue from './AnimatedValue';
import AnimatedWithChildren from './AnimatedWithChildren';
export default class AnimatedAddition extends AnimatedWithChildren {
class AnimatedAddition extends AnimatedWithChildren {
_a: AnimatedNode;
_b: AnimatedNode;
@@ -38,9 +38,7 @@ export default class AnimatedAddition extends AnimatedWithChildren {
return this._a.__getValue() + this._b.__getValue();
}
interpolate<OutputT: number | string>(
config: InterpolationConfigType<OutputT>,
): AnimatedInterpolation<OutputT> {
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}
@@ -62,3 +60,5 @@ export default class AnimatedAddition extends AnimatedWithChildren {
};
}
}
module.exports = AnimatedAddition;
+266
View File
@@ -0,0 +1,266 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
import AnimatedValue from './AnimatedValue';
import AnimatedWithChildren from './AnimatedWithChildren';
import normalizeColor from '../../StyleSheet/normalizeColor';
import {processColorObject} from '../../StyleSheet/PlatformColorValueTypes';
import type {ColorValue} from '../../StyleSheet/StyleSheet';
import type {NativeColorValue} from '../../StyleSheet/PlatformColorValueTypes';
type ColorListenerCallback = (value: string) => mixed;
type RgbaValue = {
+r: number,
+g: number,
+b: number,
+a: number,
...
};
type RgbaAnimatedValue = {
+r: AnimatedValue,
+g: AnimatedValue,
+b: AnimatedValue,
+a: AnimatedValue,
...
};
const defaultColor: RgbaValue = {r: 0, g: 0, b: 0, a: 1.0};
let _uniqueId = 1;
/* eslint no-bitwise: 0 */
function processColor(color?: ?ColorValue): ?(RgbaValue | NativeColorValue) {
if (color === undefined || color === null) {
return null;
}
let normalizedColor = normalizeColor(color);
if (normalizedColor === undefined || normalizedColor === null) {
return null;
}
if (typeof normalizedColor === 'object') {
const processedColorObj = processColorObject(normalizedColor);
if (processedColorObj != null) {
return processedColorObj;
}
} else if (typeof normalizedColor === 'number') {
const r = (normalizedColor & 0xff000000) >>> 24;
const g = (normalizedColor & 0x00ff0000) >>> 16;
const b = (normalizedColor & 0x0000ff00) >>> 8;
const a = (normalizedColor & 0x000000ff) / 255;
return {r, g, b, a};
}
return null;
}
function isRgbaValue(value: any): boolean {
return (
value &&
typeof value.r === 'number' &&
typeof value.g === 'number' &&
typeof value.b === 'number' &&
typeof value.a === 'number'
);
}
function isRgbaAnimatedValue(value: any): boolean {
return (
value &&
value.r instanceof AnimatedValue &&
value.g instanceof AnimatedValue &&
value.b instanceof AnimatedValue &&
value.a instanceof AnimatedValue
);
}
export default class AnimatedColor extends AnimatedWithChildren {
r: AnimatedValue;
g: AnimatedValue;
b: AnimatedValue;
a: AnimatedValue;
_listeners: {
[key: string]: {
r: string,
g: string,
b: string,
a: string,
...
},
...
};
constructor(valueIn?: ?(RgbaValue | RgbaAnimatedValue | ColorValue)) {
super();
let value: RgbaValue | RgbaAnimatedValue | ColorValue =
valueIn || defaultColor;
if (isRgbaAnimatedValue(value)) {
// $FlowIgnore[incompatible-cast] - Type is verified above
const rgbaAnimatedValue: RgbaAnimatedValue = (value: RgbaAnimatedValue);
this.r = rgbaAnimatedValue.r;
this.g = rgbaAnimatedValue.g;
this.b = rgbaAnimatedValue.b;
this.a = rgbaAnimatedValue.a;
} else {
// Handle potential parsable string color or platform color object
if (!isRgbaValue(value)) {
// $FlowIgnore[incompatible-cast] - Type is verified via conditionals
value = processColor((value: ColorValue)) || {r: 0, g: 0, b: 0, a: 1.0};
// TODO: support platform color
}
// $FlowIgnore[incompatible-cast] - Type is verified via conditionals
const rgbaValue: RgbaValue = (value: RgbaValue);
this.r = new AnimatedValue(rgbaValue.r);
this.g = new AnimatedValue(rgbaValue.g);
this.b = new AnimatedValue(rgbaValue.b);
this.a = new AnimatedValue(rgbaValue.a);
}
this._listeners = {};
}
/**
* Directly set the value. This will stop any animations running on the value
* and update all the bound properties.
*/
setValue(value: {r: number, g: number, b: number, a: number, ...}): void {
this.r.setValue(value.r);
this.g.setValue(value.g);
this.b.setValue(value.b);
this.a.setValue(value.a);
}
/**
* Sets an offset that is applied on top of whatever value is set, whether
* via `setValue`, an animation, or `Animated.event`. Useful for compensating
* things like the start of a pan gesture.
*/
setOffset(offset: {r: number, g: number, b: number, a: number, ...}): void {
this.r.setOffset(offset.r);
this.g.setOffset(offset.g);
this.b.setOffset(offset.b);
this.a.setOffset(offset.a);
}
/**
* Merges the offset value into the base value and resets the offset to zero.
* The final output of the value is unchanged.
*/
flattenOffset(): void {
this.r.flattenOffset();
this.g.flattenOffset();
this.b.flattenOffset();
this.a.flattenOffset();
}
/**
* Sets the offset value to the base value, and resets the base value to
* zero. The final output of the value is unchanged.
*/
extractOffset(): void {
this.r.extractOffset();
this.g.extractOffset();
this.b.extractOffset();
this.a.extractOffset();
}
/**
* Adds an asynchronous listener to the value so you can observe updates from
* animations. This is useful because there is no way to synchronously read
* the value because it might be driven natively.
*
* Returns a string that serves as an identifier for the listener.
*/
addListener(callback: ColorListenerCallback): string {
const id = String(_uniqueId++);
const jointCallback = ({value: number}) => {
callback(this.__getValue());
};
this._listeners[id] = {
r: this.r.addListener(jointCallback),
g: this.g.addListener(jointCallback),
b: this.b.addListener(jointCallback),
a: this.a.addListener(jointCallback),
};
return id;
}
/**
* Unregister a listener. The `id` param shall match the identifier
* previously returned by `addListener()`.
*/
removeListener(id: string): void {
this.r.removeListener(this._listeners[id].r);
this.g.removeListener(this._listeners[id].g);
this.b.removeListener(this._listeners[id].b);
this.a.removeListener(this._listeners[id].a);
delete this._listeners[id];
}
/**
* Remove all registered listeners.
*/
removeAllListeners(): void {
this.r.removeAllListeners();
this.g.removeAllListeners();
this.b.removeAllListeners();
this.a.removeAllListeners();
this._listeners = {};
}
/**
* Stops any running animation or tracking. `callback` is invoked with the
* final value after stopping the animation, which is useful for updating
* state to match the animation position with layout.
*/
stopAnimation(callback?: (value: string) => void): void {
this.r.stopAnimation();
this.g.stopAnimation();
this.b.stopAnimation();
this.a.stopAnimation();
callback && callback(this.__getValue());
}
/**
* Stops any animation and resets the value to its original.
*/
resetAnimation(callback?: (value: string) => void): void {
this.r.resetAnimation();
this.g.resetAnimation();
this.b.resetAnimation();
this.a.resetAnimation();
callback && callback(this.__getValue());
}
__getValue(): string {
return `rgba(${this.r.__getValue()}, ${this.g.__getValue()}, ${this.b.__getValue()}, ${this.a.__getValue()})`;
}
__attach(): void {
this.r.__addChild(this);
this.g.__addChild(this);
this.b.__addChild(this);
this.a.__addChild(this);
super.__attach();
}
__detach(): void {
this.r.__removeChild(this);
this.g.__removeChild(this);
this.b.__removeChild(this);
this.a.__removeChild(this);
super.__detach();
}
}
@@ -10,14 +10,14 @@
'use strict';
import type {PlatformConfig} from '../AnimatedPlatformConfig';
const AnimatedInterpolation = require('./AnimatedInterpolation');
const AnimatedNode = require('./AnimatedNode');
const AnimatedWithChildren = require('./AnimatedWithChildren');
import type {InterpolationConfigType} from './AnimatedInterpolation';
import type AnimatedNode from './AnimatedNode';
import type {PlatformConfig} from '../AnimatedPlatformConfig';
import AnimatedInterpolation from './AnimatedInterpolation';
import AnimatedWithChildren from './AnimatedWithChildren';
export default class AnimatedDiffClamp extends AnimatedWithChildren {
class AnimatedDiffClamp extends AnimatedWithChildren {
_a: AnimatedNode;
_min: number;
_max: number;
@@ -38,9 +38,7 @@ export default class AnimatedDiffClamp extends AnimatedWithChildren {
super.__makeNative(platformConfig);
}
interpolate<OutputT: number | string>(
config: InterpolationConfigType<OutputT>,
): AnimatedInterpolation<OutputT> {
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}
@@ -70,3 +68,5 @@ export default class AnimatedDiffClamp extends AnimatedWithChildren {
};
}
}
module.exports = AnimatedDiffClamp;
@@ -10,15 +10,15 @@
'use strict';
import type {PlatformConfig} from '../AnimatedPlatformConfig';
const AnimatedInterpolation = require('./AnimatedInterpolation');
const AnimatedNode = require('./AnimatedNode');
const AnimatedValue = require('./AnimatedValue');
const AnimatedWithChildren = require('./AnimatedWithChildren');
import type {InterpolationConfigType} from './AnimatedInterpolation';
import type {PlatformConfig} from '../AnimatedPlatformConfig';
import AnimatedInterpolation from './AnimatedInterpolation';
import AnimatedNode from './AnimatedNode';
import AnimatedValue from './AnimatedValue';
import AnimatedWithChildren from './AnimatedWithChildren';
export default class AnimatedDivision extends AnimatedWithChildren {
class AnimatedDivision extends AnimatedWithChildren {
_a: AnimatedNode;
_b: AnimatedNode;
_warnedAboutDivideByZero: boolean = false;
@@ -54,9 +54,7 @@ export default class AnimatedDivision extends AnimatedWithChildren {
return a / b;
}
interpolate<OutputT: number | string>(
config: InterpolationConfigType<OutputT>,
): AnimatedInterpolation<OutputT> {
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}
@@ -78,3 +76,5 @@ export default class AnimatedDivision extends AnimatedWithChildren {
};
}
}
module.exports = AnimatedDivision;
@@ -0,0 +1,374 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
/* eslint no-bitwise: 0 */
'use strict';
const AnimatedNode = require('./AnimatedNode');
const AnimatedWithChildren = require('./AnimatedWithChildren');
const NativeAnimatedHelper = require('../NativeAnimatedHelper');
const invariant = require('invariant');
const normalizeColor = require('../../StyleSheet/normalizeColor');
import type {PlatformConfig} from '../AnimatedPlatformConfig';
type ExtrapolateType = 'extend' | 'identity' | 'clamp';
export type InterpolationConfigType = {
inputRange: $ReadOnlyArray<number>,
outputRange: $ReadOnlyArray<number> | $ReadOnlyArray<string>,
easing?: (input: number) => number,
extrapolate?: ExtrapolateType,
extrapolateLeft?: ExtrapolateType,
extrapolateRight?: ExtrapolateType,
};
const linear = (t: number) => t;
/**
* Very handy helper to map input ranges to output ranges with an easing
* function and custom behavior outside of the ranges.
*/
function createInterpolation(
config: InterpolationConfigType,
): (input: number) => number | string {
if (config.outputRange && typeof config.outputRange[0] === 'string') {
return createInterpolationFromStringOutputRange(config);
}
const outputRange: Array<number> = (config.outputRange: any);
checkInfiniteRange('outputRange', outputRange);
const inputRange = config.inputRange;
checkInfiniteRange('inputRange', inputRange);
checkValidInputRange(inputRange);
invariant(
inputRange.length === outputRange.length,
'inputRange (' +
inputRange.length +
') and outputRange (' +
outputRange.length +
') must have the same length',
);
const easing = config.easing || linear;
let extrapolateLeft: ExtrapolateType = 'extend';
if (config.extrapolateLeft !== undefined) {
extrapolateLeft = config.extrapolateLeft;
} else if (config.extrapolate !== undefined) {
extrapolateLeft = config.extrapolate;
}
let extrapolateRight: ExtrapolateType = 'extend';
if (config.extrapolateRight !== undefined) {
extrapolateRight = config.extrapolateRight;
} else if (config.extrapolate !== undefined) {
extrapolateRight = config.extrapolate;
}
return input => {
invariant(
typeof input === 'number',
'Cannot interpolation an input which is not a number',
);
const range = findRange(input, inputRange);
return interpolate(
input,
inputRange[range],
inputRange[range + 1],
outputRange[range],
outputRange[range + 1],
easing,
extrapolateLeft,
extrapolateRight,
);
};
}
function interpolate(
input: number,
inputMin: number,
inputMax: number,
outputMin: number,
outputMax: number,
easing: (input: number) => number,
extrapolateLeft: ExtrapolateType,
extrapolateRight: ExtrapolateType,
) {
let result = input;
// Extrapolate
if (result < inputMin) {
if (extrapolateLeft === 'identity') {
return result;
} else if (extrapolateLeft === 'clamp') {
result = inputMin;
} else if (extrapolateLeft === 'extend') {
// noop
}
}
if (result > inputMax) {
if (extrapolateRight === 'identity') {
return result;
} else if (extrapolateRight === 'clamp') {
result = inputMax;
} else if (extrapolateRight === 'extend') {
// noop
}
}
if (outputMin === outputMax) {
return outputMin;
}
if (inputMin === inputMax) {
if (input <= inputMin) {
return outputMin;
}
return outputMax;
}
// Input Range
if (inputMin === -Infinity) {
result = -result;
} else if (inputMax === Infinity) {
result = result - inputMin;
} else {
result = (result - inputMin) / (inputMax - inputMin);
}
// Easing
result = easing(result);
// Output Range
if (outputMin === -Infinity) {
result = -result;
} else if (outputMax === Infinity) {
result = result + outputMin;
} else {
result = result * (outputMax - outputMin) + outputMin;
}
return result;
}
function colorToRgba(input: string): string {
let normalizedColor = normalizeColor(input);
if (normalizedColor === null || typeof normalizedColor !== 'number') {
return input;
}
normalizedColor = normalizedColor || 0;
const r = (normalizedColor & 0xff000000) >>> 24;
const g = (normalizedColor & 0x00ff0000) >>> 16;
const b = (normalizedColor & 0x0000ff00) >>> 8;
const a = (normalizedColor & 0x000000ff) / 255;
return `rgba(${r}, ${g}, ${b}, ${a})`;
}
const stringShapeRegex = /[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/g;
/**
* Supports string shapes by extracting numbers so new values can be computed,
* and recombines those values into new strings of the same shape. Supports
* things like:
*
* rgba(123, 42, 99, 0.36) // colors
* -45deg // values with units
*/
function createInterpolationFromStringOutputRange(
config: InterpolationConfigType,
): (input: number) => string {
let outputRange: Array<string> = (config.outputRange: any);
invariant(outputRange.length >= 2, 'Bad output range');
outputRange = outputRange.map(colorToRgba);
checkPattern(outputRange);
// ['rgba(0, 100, 200, 0)', 'rgba(50, 150, 250, 0.5)']
// ->
// [
// [0, 50],
// [100, 150],
// [200, 250],
// [0, 0.5],
// ]
/* $FlowFixMe[incompatible-use] (>=0.18.0): `outputRange[0].match()` can
* return `null`. Need to guard against this possibility. */
const outputRanges = outputRange[0].match(stringShapeRegex).map(() => []);
outputRange.forEach(value => {
/* $FlowFixMe[incompatible-use] (>=0.18.0): `value.match()` can return
* `null`. Need to guard against this possibility. */
value.match(stringShapeRegex).forEach((number, i) => {
outputRanges[i].push(+number);
});
});
const interpolations = outputRange[0]
.match(stringShapeRegex)
/* $FlowFixMe[incompatible-use] (>=0.18.0): `outputRange[0].match()` can
* return `null`. Need to guard against this possibility. */
/* $FlowFixMe[incompatible-call] (>=0.18.0): `outputRange[0].match()` can
* return `null`. Need to guard against this possibility. */
.map((value, i) => {
return createInterpolation({
...config,
outputRange: outputRanges[i],
});
});
// rgba requires that the r,g,b are integers.... so we want to round them, but we *dont* want to
// round the opacity (4th column).
const shouldRound = isRgbOrRgba(outputRange[0]);
return input => {
let i = 0;
// 'rgba(0, 100, 200, 0)'
// ->
// 'rgba(${interpolations[0](input)}, ${interpolations[1](input)}, ...'
return outputRange[0].replace(stringShapeRegex, () => {
let val = +interpolations[i++](input);
if (shouldRound) {
val = i < 4 ? Math.round(val) : Math.round(val * 1000) / 1000;
}
return String(val);
});
};
}
function isRgbOrRgba(range: string) {
return typeof range === 'string' && range.startsWith('rgb');
}
function checkPattern(arr: $ReadOnlyArray<string>) {
const pattern = arr[0].replace(stringShapeRegex, '');
for (let i = 1; i < arr.length; ++i) {
invariant(
pattern === arr[i].replace(stringShapeRegex, ''),
'invalid pattern ' + arr[0] + ' and ' + arr[i],
);
}
}
function findRange(input: number, inputRange: $ReadOnlyArray<number>) {
let i;
for (i = 1; i < inputRange.length - 1; ++i) {
if (inputRange[i] >= input) {
break;
}
}
return i - 1;
}
function checkValidInputRange(arr: $ReadOnlyArray<number>) {
invariant(arr.length >= 2, 'inputRange must have at least 2 elements');
for (let i = 1; i < arr.length; ++i) {
invariant(
arr[i] >= arr[i - 1],
/* $FlowFixMe[incompatible-type] (>=0.13.0) - In the addition expression
* below this comment, one or both of the operands may be something that
* doesn't cleanly convert to a string, like undefined, null, and object,
* etc. If you really mean this implicit string conversion, you can do
* something like String(myThing) */
'inputRange must be monotonically non-decreasing ' + arr,
);
}
}
function checkInfiniteRange(name: string, arr: $ReadOnlyArray<number>) {
invariant(arr.length >= 2, name + ' must have at least 2 elements');
invariant(
arr.length !== 2 || arr[0] !== -Infinity || arr[1] !== Infinity,
/* $FlowFixMe[incompatible-type] (>=0.13.0) - In the addition expression
* below this comment, one or both of the operands may be something that
* doesn't cleanly convert to a string, like undefined, null, and object,
* etc. If you really mean this implicit string conversion, you can do
* something like String(myThing) */
name + 'cannot be ]-infinity;+infinity[ ' + arr,
);
}
class AnimatedInterpolation extends AnimatedWithChildren {
// Export for testing.
static __createInterpolation: (
config: InterpolationConfigType,
) => (input: number) => number | string = createInterpolation;
_parent: AnimatedNode;
_config: InterpolationConfigType;
_interpolation: (input: number) => number | string;
constructor(parent: AnimatedNode, config: InterpolationConfigType) {
super();
this._parent = parent;
this._config = config;
this._interpolation = createInterpolation(config);
}
__makeNative(platformConfig: ?PlatformConfig) {
this._parent.__makeNative(platformConfig);
super.__makeNative(platformConfig);
}
__getValue(): number | string {
const parentValue: number = this._parent.__getValue();
invariant(
typeof parentValue === 'number',
'Cannot interpolate an input which is not a number.',
);
return this._interpolation(parentValue);
}
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}
__attach(): void {
this._parent.__addChild(this);
}
__detach(): void {
this._parent.__removeChild(this);
super.__detach();
}
__transformDataType(range: Array<any>): Array<any> {
return range.map(NativeAnimatedHelper.transformDataType);
}
__getNativeConfig(): any {
if (__DEV__) {
NativeAnimatedHelper.validateInterpolation(this._config);
}
return {
inputRange: this._config.inputRange,
// Only the `outputRange` can contain strings so we don't need to transform `inputRange` here
/* $FlowFixMe[incompatible-call] (>=0.38.0) - Flow error detected during
* the deployment of v0.38.0. To see the error, remove this comment and
* run flow */
outputRange: this.__transformDataType(this._config.outputRange),
extrapolateLeft:
this._config.extrapolateLeft || this._config.extrapolate || 'extend',
extrapolateRight:
this._config.extrapolateRight || this._config.extrapolate || 'extend',
type: 'interpolation',
};
}
}
module.exports = AnimatedInterpolation;

Some files were not shown because too many files have changed in this diff Show More