react-native 0.85.0 → 0.85.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/Libraries/Core/ReactNativeVersion.js +1 -1
  2. package/React/Base/RCTVersion.m +1 -1
  3. package/React/CoreModules/RCTDevLoadingView.mm +17 -0
  4. package/ReactAndroid/gradle.properties +1 -1
  5. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android.kt +5 -1
  6. package/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkEventUtil.kt +8 -0
  7. package/ReactAndroid/src/main/java/com/facebook/react/modules/network/RequestBodyUtil.kt +2 -0
  8. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
  9. package/ReactCommon/cxxreact/ReactNativeVersion.h +2 -2
  10. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSExperimental.h +9 -1
  11. package/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp +4 -1
  12. package/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp +58 -25
  13. package/ReactCommon/react/renderer/animationbackend/AnimationBackend.h +9 -0
  14. package/ReactCommon/react/renderer/animationbackend/AnimationChoreographer.h +5 -0
  15. package/ReactCommon/react/renderer/uimanager/UIManagerAnimationBackend.h +1 -0
  16. package/package.json +9 -9
  17. package/scripts/codegen/generate-artifacts-executor/generateAppDependencyProvider.js +4 -4
  18. package/scripts/codegen/generate-artifacts-executor/generateCustomURLHandlers.js +3 -3
  19. package/scripts/codegen/generate-artifacts-executor/generateNativeCode.js +2 -3
  20. package/scripts/codegen/generate-artifacts-executor/generatePackageSwift.js +2 -2
  21. package/scripts/codegen/generate-artifacts-executor/generateRCTModuleProviders.js +3 -2
  22. package/scripts/codegen/generate-artifacts-executor/generateRCTThirdPartyComponents.js +3 -2
  23. package/scripts/codegen/generate-artifacts-executor/generateReactCodegenPodspec.js +2 -2
  24. package/scripts/codegen/generate-artifacts-executor/generateUnstableModulesRequiringMainQueueSetupProvider.js +3 -3
  25. package/scripts/codegen/generate-artifacts-executor/utils.js +48 -0
@@ -28,7 +28,7 @@
28
28
  export default class ReactNativeVersion {
29
29
  static major: number = 0;
30
30
  static minor: number = 85;
31
- static patch: number = 0;
31
+ static patch: number = 2;
32
32
  static prerelease: string | null = null;
33
33
 
34
34
  static getVersionString(): string {
@@ -23,7 +23,7 @@ NSDictionary* RCTGetReactNativeVersion(void)
23
23
  __rnVersion = @{
24
24
  RCTVersionMajor: @(0),
25
25
  RCTVersionMinor: @(85),
26
- RCTVersionPatch: @(0),
26
+ RCTVersionPatch: @(2),
27
27
  RCTVersionPrerelease: [NSNull null],
28
28
  };
29
29
  });
@@ -51,10 +51,27 @@ RCT_EXPORT_MODULE()
51
51
  selector:@selector(hide)
52
52
  name:RCTJavaScriptDidFailToLoadNotification
53
53
  object:nil];
54
+ [[NSNotificationCenter defaultCenter] addObserver:self
55
+ selector:@selector(hide)
56
+ name:@"RCTInstanceDidLoadBundle"
57
+ object:nil];
54
58
  }
55
59
  return self;
56
60
  }
57
61
 
62
+ - (void)dealloc
63
+ {
64
+ [self clearInitialMessageDelay];
65
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
66
+ UIWindow *window = _window;
67
+ _window = nil;
68
+ if (window) {
69
+ RCTExecuteOnMainQueue(^{
70
+ window.hidden = YES;
71
+ });
72
+ }
73
+ }
74
+
58
75
  + (void)setEnabled:(BOOL)enabled
59
76
  {
60
77
  RCTDevLoadingViewSetEnabled(enabled);
@@ -1,4 +1,4 @@
1
- VERSION_NAME=0.85.0
1
+ VERSION_NAME=0.85.2
2
2
  react.internal.publishingGroup=com.facebook.react
3
3
  react.internal.hermesPublishingGroup=com.facebook.hermes
4
4
 
@@ -4,7 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @generated SignedSource<<8531ce29d0e5362517d35559ebda623b>>
7
+ * @generated SignedSource<<2452c003ffcba8e20b7cd40c68e05e3d>>
8
8
  */
9
9
 
10
10
  /**
@@ -23,6 +23,8 @@ public open class ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android :
23
23
  // We could use JNI to get the defaults from C++,
24
24
  // but that is more expensive than just duplicating the defaults here.
25
25
 
26
+ override fun cxxNativeAnimatedEnabled(): Boolean = true
27
+
26
28
  override fun enableAccessibilityOrder(): Boolean = true
27
29
 
28
30
  override fun enableSwiftUIBasedFilters(): Boolean = true
@@ -30,4 +32,6 @@ public open class ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android :
30
32
  override fun fixTextClippingAndroid15useBoundsForWidth(): Boolean = true
31
33
 
32
34
  override fun preventShadowTreeCommitExhaustion(): Boolean = true
35
+
36
+ override fun useSharedAnimatedBackend(): Boolean = true
33
37
  }
@@ -19,6 +19,7 @@ import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
19
19
  import java.io.IOException
20
20
  import java.net.SocketTimeoutException
21
21
  import okhttp3.Headers
22
+ import okhttp3.MultipartBody
22
23
  import okhttp3.RequestBody
23
24
  import okio.Buffer
24
25
 
@@ -263,6 +264,13 @@ internal object NetworkEventUtil {
263
264
  return "[Preview unavailable]"
264
265
  }
265
266
 
267
+ // MultipartBody does not propagate isOneShot() from its parts, so check each
268
+ // part explicitly. Reading a one-shot part here would drain the underlying
269
+ // stream and cause the real request to fail.
270
+ if (body is MultipartBody && body.parts().any { it.body().isOneShot() }) {
271
+ return "[Preview unavailable]"
272
+ }
273
+
266
274
  return try {
267
275
  val buffer = Buffer()
268
276
  body.writeTo(buffer)
@@ -146,6 +146,8 @@ internal object RequestBodyUtil {
146
146
  }
147
147
  }
148
148
 
149
+ override fun isOneShot(): Boolean = true
150
+
149
151
  @Throws(IOException::class)
150
152
  override fun writeTo(sink: BufferedSink) {
151
153
  var source: Source? = null
@@ -14,7 +14,7 @@ public object ReactNativeVersion {
14
14
  public val VERSION: Map<String, Any?> = mapOf(
15
15
  "major" to 0,
16
16
  "minor" to 85,
17
- "patch" to 0,
17
+ "patch" to 2,
18
18
  "prerelease" to null
19
19
  )
20
20
  }
@@ -14,14 +14,14 @@
14
14
 
15
15
  #define REACT_NATIVE_VERSION_MAJOR 0
16
16
  #define REACT_NATIVE_VERSION_MINOR 85
17
- #define REACT_NATIVE_VERSION_PATCH 0
17
+ #define REACT_NATIVE_VERSION_PATCH 2
18
18
 
19
19
  namespace facebook::react {
20
20
 
21
21
  struct ReactNativeVersionType {
22
22
  int32_t Major = 0;
23
23
  int32_t Minor = 85;
24
- int32_t Patch = 0;
24
+ int32_t Patch = 2;
25
25
  std::string_view Prerelease = "";
26
26
  };
27
27
 
@@ -4,7 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @generated SignedSource<<6a047fa1d33ea17ebd7ba8d0680ee1cc>>
7
+ * @generated SignedSource<<6ded821dda8049a32168bf82333dd4c3>>
8
8
  */
9
9
 
10
10
  /**
@@ -27,6 +27,10 @@ class ReactNativeFeatureFlagsOverridesOSSExperimental : public ReactNativeFeatur
27
27
  public:
28
28
  ReactNativeFeatureFlagsOverridesOSSExperimental() = default;
29
29
 
30
+ bool cxxNativeAnimatedEnabled() override {
31
+ return true;
32
+ }
33
+
30
34
  bool enableAccessibilityOrder() override {
31
35
  return true;
32
36
  }
@@ -42,6 +46,10 @@ class ReactNativeFeatureFlagsOverridesOSSExperimental : public ReactNativeFeatur
42
46
  bool preventShadowTreeCommitExhaustion() override {
43
47
  return true;
44
48
  }
49
+
50
+ bool useSharedAnimatedBackend() override {
51
+ return true;
52
+ }
45
53
  };
46
54
 
47
55
  } // namespace facebook::react
@@ -515,7 +515,10 @@ void NativeAnimatedNodesManager::handleAnimatedEvent(
515
515
  // frames.
516
516
  if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) {
517
517
  if (auto animationBackend = animationBackend_.lock()) {
518
- animationBackend->trigger();
518
+ animationBackend->pushAnimationMutations(
519
+ [this](AnimationTimestamp timestamp) -> AnimationMutations {
520
+ return pullAnimationMutations(timestamp);
521
+ });
519
522
  }
520
523
  } else {
521
524
  onRender();
@@ -24,10 +24,17 @@ static inline Props::Shared cloneProps(
24
24
  shadowNode.getSurfaceId(), *shadowNode.getContextContainer()};
25
25
  Props::Shared newProps;
26
26
  if (animatedProps.rawProps) {
27
- newProps = shadowNode.getComponentDescriptor().cloneProps(
28
- propsParserContext,
29
- shadowNode.getProps(),
30
- std::move(*animatedProps.rawProps));
27
+ if (ReactNativeFeatureFlags::enableFabricCommitBranching()) {
28
+ newProps = shadowNode.getComponentDescriptor().cloneProps(
29
+ propsParserContext,
30
+ shadowNode.getProps(),
31
+ std::move(*animatedProps.rawProps));
32
+ } else {
33
+ newProps = shadowNode.getComponentDescriptor().cloneProps(
34
+ propsParserContext,
35
+ shadowNode.getProps(),
36
+ RawProps(*animatedProps.rawProps));
37
+ }
31
38
  } else {
32
39
  newProps = shadowNode.getComponentDescriptor().cloneProps(
33
40
  propsParserContext, shadowNode.getProps(), {});
@@ -51,31 +58,27 @@ AnimationBackend::AnimationBackend(
51
58
  react_native_assert(uiManager_.expired() == false);
52
59
  }
53
60
 
54
- void AnimationBackend::onAnimationFrame(AnimationTimestamp timestamp) {
55
- std::vector<CallbackWithId> callbacksCopy;
56
- std::unordered_map<SurfaceId, SurfaceUpdates> surfaceUpdates;
57
- std::set<SurfaceId> asyncFlushSurfaces;
61
+ void AnimationBackend::unpackMutations(
62
+ AnimationMutations& mutations,
63
+ std::unordered_map<SurfaceId, SurfaceUpdates>& surfaceUpdates,
64
+ std::set<SurfaceId>& asyncFlushSurfaces) {
65
+ for (auto& mutation : mutations.batch) {
66
+ const auto family = mutation.family;
67
+ react_native_assert(family != nullptr);
58
68
 
59
- {
60
- std::lock_guard lock(mutex_);
61
- callbacksCopy = callbacks;
69
+ auto& [families, updates, hasLayoutUpdates] =
70
+ surfaceUpdates[family->getSurfaceId()];
71
+ hasLayoutUpdates |= mutation.hasLayoutUpdates;
72
+ families.insert(family);
73
+ updates[mutation.tag] = std::move(mutation.props);
62
74
  }
63
75
 
64
- for (auto& callbackWithId : callbacksCopy) {
65
- auto mutations = callbackWithId.callback(timestamp);
66
- asyncFlushSurfaces.merge(mutations.asyncFlushSurfaces);
67
- for (auto& mutation : mutations.batch) {
68
- const auto family = mutation.family;
69
- react_native_assert(family != nullptr);
70
-
71
- auto& [families, updates, hasLayoutUpdates] =
72
- surfaceUpdates[family->getSurfaceId()];
73
- hasLayoutUpdates |= mutation.hasLayoutUpdates;
74
- families.insert(family);
75
- updates[mutation.tag] = std::move(mutation.props);
76
- }
77
- }
76
+ asyncFlushSurfaces.merge(mutations.asyncFlushSurfaces);
77
+ }
78
78
 
79
+ void AnimationBackend::applySurfaceUpdates(
80
+ std::unordered_map<SurfaceId, SurfaceUpdates>& surfaceUpdates,
81
+ const std::set<SurfaceId>& asyncFlushSurfaces) {
79
82
  animatedPropsRegistry_->update(surfaceUpdates);
80
83
 
81
84
  for (auto& [surfaceId, updates] : surfaceUpdates) {
@@ -89,6 +92,30 @@ void AnimationBackend::onAnimationFrame(AnimationTimestamp timestamp) {
89
92
  requestAsyncFlushForSurfaces(asyncFlushSurfaces);
90
93
  }
91
94
 
95
+ void AnimationBackend::applyMutations(AnimationMutations mutations) {
96
+ std::unordered_map<SurfaceId, SurfaceUpdates> surfaceUpdates;
97
+ std::set<SurfaceId> asyncFlushSurfaces;
98
+ unpackMutations(mutations, surfaceUpdates, asyncFlushSurfaces);
99
+ applySurfaceUpdates(surfaceUpdates, asyncFlushSurfaces);
100
+ }
101
+
102
+ void AnimationBackend::onAnimationFrame(AnimationTimestamp timestamp) {
103
+ std::vector<CallbackWithId> callbacksCopy;
104
+
105
+ {
106
+ std::lock_guard lock(mutex_);
107
+ callbacksCopy = callbacks;
108
+ }
109
+
110
+ std::unordered_map<SurfaceId, SurfaceUpdates> surfaceUpdates;
111
+ std::set<SurfaceId> asyncFlushSurfaces;
112
+ for (auto& callbackWithId : callbacksCopy) {
113
+ auto mutations = callbackWithId.callback(timestamp);
114
+ unpackMutations(mutations, surfaceUpdates, asyncFlushSurfaces);
115
+ }
116
+ applySurfaceUpdates(surfaceUpdates, asyncFlushSurfaces);
117
+ }
118
+
92
119
  CallbackId AnimationBackend::start(const Callback& callback) {
93
120
  std::lock_guard lock(mutex_);
94
121
 
@@ -123,6 +150,12 @@ void AnimationBackend::trigger() {
123
150
  onAnimationFrame(std::chrono::steady_clock::now().time_since_epoch());
124
151
  }
125
152
 
153
+ void AnimationBackend::pushAnimationMutations(const Callback& callback) {
154
+ auto timestamp = animationChoreographer_->now();
155
+ auto mutations = callback(timestamp);
156
+ applyMutations(std::move(mutations));
157
+ }
158
+
126
159
  void AnimationBackend::commitUpdates(
127
160
  SurfaceId surfaceId,
128
161
  SurfaceUpdates& surfaceUpdates) {
@@ -60,10 +60,19 @@ class AnimationBackend : public UIManagerAnimationBackend {
60
60
 
61
61
  void onAnimationFrame(AnimationTimestamp timestamp) override;
62
62
  void trigger() override;
63
+ void pushAnimationMutations(const Callback &callback) override;
63
64
  CallbackId start(const Callback &callback) override;
64
65
  void stop(CallbackId callbackId) override;
65
66
 
66
67
  private:
68
+ void unpackMutations(
69
+ AnimationMutations &mutations,
70
+ std::unordered_map<SurfaceId, SurfaceUpdates> &surfaceUpdates,
71
+ std::set<SurfaceId> &asyncFlushSurfaces);
72
+ void applySurfaceUpdates(
73
+ std::unordered_map<SurfaceId, SurfaceUpdates> &surfaceUpdates,
74
+ const std::set<SurfaceId> &asyncFlushSurfaces);
75
+ void applyMutations(AnimationMutations mutations);
67
76
  std::vector<CallbackWithId> callbacks;
68
77
  std::shared_ptr<AnimatedPropsRegistry> animatedPropsRegistry_;
69
78
  std::shared_ptr<AnimationChoreographer> animationChoreographer_;
@@ -8,6 +8,7 @@
8
8
  #pragma once
9
9
 
10
10
  #include <react/renderer/uimanager/UIManagerAnimationBackend.h>
11
+ #include <react/timing/primitives.h>
11
12
 
12
13
  namespace facebook::react {
13
14
 
@@ -21,6 +22,10 @@ class AnimationChoreographer {
21
22
 
22
23
  virtual void resume() = 0;
23
24
  virtual void pause() = 0;
25
+ virtual AnimationTimestamp now() const
26
+ {
27
+ return HighResTimeStamp::now().toChronoSteadyClockTimePoint().time_since_epoch();
28
+ }
24
29
  void setAnimationBackend(std::weak_ptr<UIManagerAnimationBackend> animationBackend)
25
30
  {
26
31
  animationBackend_ = animationBackend;
@@ -31,6 +31,7 @@ class UIManagerAnimationBackend {
31
31
  virtual void stop(CallbackId callbackId) = 0;
32
32
  virtual void clearRegistry(SurfaceId surfaceId) = 0;
33
33
  virtual void trigger() = 0;
34
+ virtual void pushAnimationMutations(const Callback &callback) = 0;
34
35
  virtual void registerJSInvoker(std::shared_ptr<CallInvoker> jsInvoker) = 0;
35
36
  };
36
37
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native",
3
- "version": "0.85.0",
3
+ "version": "0.85.2",
4
4
  "description": "A framework for building native apps using React",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -149,7 +149,7 @@
149
149
  "featureflags": "node ./scripts/featureflags/index.js"
150
150
  },
151
151
  "peerDependencies": {
152
- "@react-native/jest-preset": "0.85.0",
152
+ "@react-native/jest-preset": "0.85.2",
153
153
  "@types/react": "^19.1.1",
154
154
  "react": "^19.2.3"
155
155
  },
@@ -162,13 +162,13 @@
162
162
  }
163
163
  },
164
164
  "dependencies": {
165
- "@react-native/assets-registry": "0.85.0",
166
- "@react-native/codegen": "0.85.0",
167
- "@react-native/community-cli-plugin": "0.85.0",
168
- "@react-native/gradle-plugin": "0.85.0",
169
- "@react-native/js-polyfills": "0.85.0",
170
- "@react-native/normalize-colors": "0.85.0",
171
- "@react-native/virtualized-lists": "0.85.0",
165
+ "@react-native/assets-registry": "0.85.2",
166
+ "@react-native/codegen": "0.85.2",
167
+ "@react-native/community-cli-plugin": "0.85.2",
168
+ "@react-native/gradle-plugin": "0.85.2",
169
+ "@react-native/js-polyfills": "0.85.2",
170
+ "@react-native/normalize-colors": "0.85.2",
171
+ "@react-native/virtualized-lists": "0.85.2",
172
172
  "abort-controller": "^3.0.0",
173
173
  "anser": "^1.4.9",
174
174
  "ansi-regex": "^5.0.0",
@@ -10,7 +10,7 @@
10
10
 
11
11
  'use strict';
12
12
  const {TEMPLATES_FOLDER_PATH, packageJson} = require('./constants');
13
- const {codegenLog} = require('./utils');
13
+ const {codegenLog, writeFileSyncIfChanged} = require('./utils');
14
14
  const fs = require('fs');
15
15
  const path = require('path');
16
16
 
@@ -38,7 +38,7 @@ function generateAppDependencyProvider(outputDir /*: string */) {
38
38
  'utf8',
39
39
  );
40
40
  const finalPathH = path.join(outputDir, 'RCTAppDependencyProvider.h');
41
- fs.writeFileSync(finalPathH, templateH);
41
+ writeFileSyncIfChanged(finalPathH, templateH);
42
42
  codegenLog(`Generated artifact: ${finalPathH}`);
43
43
 
44
44
  const templateMM = fs.readFileSync(
@@ -46,7 +46,7 @@ function generateAppDependencyProvider(outputDir /*: string */) {
46
46
  'utf8',
47
47
  );
48
48
  const finalPathMM = path.join(outputDir, 'RCTAppDependencyProvider.mm');
49
- fs.writeFileSync(finalPathMM, templateMM);
49
+ writeFileSyncIfChanged(finalPathMM, templateMM);
50
50
  codegenLog(`Generated artifact: ${finalPathMM}`);
51
51
 
52
52
  // Generate the podspec file
@@ -58,7 +58,7 @@ function generateAppDependencyProvider(outputDir /*: string */) {
58
58
  outputDir,
59
59
  'ReactAppDependencyProvider.podspec',
60
60
  );
61
- fs.writeFileSync(finalPathPodspec, templatePodspec);
61
+ writeFileSyncIfChanged(finalPathPodspec, templatePodspec);
62
62
  codegenLog(`Generated podspec: ${finalPathPodspec}`);
63
63
  }
64
64
 
@@ -11,7 +11,7 @@
11
11
  'use strict';
12
12
 
13
13
  const {TEMPLATES_FOLDER_PATH} = require('./constants');
14
- const {parseiOSAnnotations} = require('./utils');
14
+ const {parseiOSAnnotations, writeFileSyncIfChanged} = require('./utils');
15
15
  const fs = require('fs');
16
16
  const path = require('path');
17
17
 
@@ -105,13 +105,13 @@ function generateCustomURLHandlers(
105
105
 
106
106
  fs.mkdirSync(outputDir, {recursive: true});
107
107
 
108
- fs.writeFileSync(
108
+ writeFileSyncIfChanged(
109
109
  path.join(outputDir, 'RCTModulesConformingToProtocolsProvider.mm'),
110
110
  finalMMFile,
111
111
  );
112
112
 
113
113
  const templateH = fs.readFileSync(MODULES_PROTOCOLS_H_TEMPLATE_PATH, 'utf8');
114
- fs.writeFileSync(
114
+ writeFileSyncIfChanged(
115
115
  path.join(outputDir, 'RCTModulesConformingToProtocolsProvider.h'),
116
116
  templateH,
117
117
  );
@@ -12,7 +12,7 @@
12
12
 
13
13
  const generateSpecsCLIExecutor = require('../generate-specs-cli-executor');
14
14
  const {CORE_LIBRARIES_WITH_OUTPUT_FOLDER} = require('./constants');
15
- const {codegenLog} = require('./utils');
15
+ const {codegenLog, cpSyncRecursiveIfChanged} = require('./utils');
16
16
  const fs = require('fs');
17
17
  const os = require('os');
18
18
  const path = require('path');
@@ -63,8 +63,7 @@ function generateCode(
63
63
  const outputDir =
64
64
  reactNativeCoreLibraryOutputPath(libraryName, platform) ?? outputPath;
65
65
  fs.mkdirSync(outputDir, {recursive: true});
66
- // $FlowFixMe[prop-missing] - `fs.cpSync` is missing in Flow libdefs.
67
- fs.cpSync(tmpOutputDir, outputDir, {recursive: true});
66
+ cpSyncRecursiveIfChanged(tmpOutputDir, outputDir);
68
67
  codegenLog(`Generated artifacts: ${outputDir}`);
69
68
  }
70
69
 
@@ -10,7 +10,7 @@
10
10
 
11
11
  'use strict';
12
12
  const {TEMPLATES_FOLDER_PATH} = require('./constants');
13
- const {codegenLog} = require('./utils');
13
+ const {codegenLog, writeFileSyncIfChanged} = require('./utils');
14
14
  const fs = require('fs');
15
15
  const path = require('path');
16
16
 
@@ -35,7 +35,7 @@ function generatePackageSwift(
35
35
  path.relative(fullOutputPath, reactNativePath),
36
36
  );
37
37
  const finalPathH = path.join(outputDir, 'Package.swift');
38
- fs.writeFileSync(finalPathH, templateH);
38
+ writeFileSyncIfChanged(finalPathH, templateH);
39
39
  codegenLog(`Generated artifact: ${finalPathH}`);
40
40
  }
41
41
 
@@ -14,6 +14,7 @@ const {
14
14
  codegenLog,
15
15
  isReactNativeCoreLibrary,
16
16
  parseiOSAnnotations,
17
+ writeFileSyncIfChanged,
17
18
  } = require('./utils');
18
19
  const fs = require('fs');
19
20
  const path = require('path');
@@ -39,7 +40,7 @@ function generateRCTModuleProviders(
39
40
  codegenLog('Generating RCTModulesProvider.h');
40
41
  const templateH = fs.readFileSync(MODULE_PROVIDERS_H_TEMPLATE_PATH, 'utf8');
41
42
  const finalPathH = path.join(outputDir, 'RCTModuleProviders.h');
42
- fs.writeFileSync(finalPathH, templateH);
43
+ writeFileSyncIfChanged(finalPathH, templateH);
43
44
  codegenLog(`Generated artifact: ${finalPathH}`);
44
45
 
45
46
  codegenLog('Generating RCTModuleProviders.mm');
@@ -112,7 +113,7 @@ function generateRCTModuleProviders(
112
113
  .readFileSync(MODULE_PROVIDERS_MM_TEMPLATE_PATH, 'utf8')
113
114
  .replace(/{moduleMapping}/, modulesMapping);
114
115
  const finalPathMM = path.join(outputDir, 'RCTModuleProviders.mm');
115
- fs.writeFileSync(finalPathMM, templateMM);
116
+ writeFileSyncIfChanged(finalPathMM, templateMM);
116
117
  codegenLog(`Generated artifact: ${finalPathMM}`);
117
118
  }
118
119
 
@@ -15,6 +15,7 @@ const {
15
15
  codegenLog,
16
16
  isReactNativeCoreLibrary,
17
17
  parseiOSAnnotations,
18
+ writeFileSyncIfChanged,
18
19
  } = require('./utils');
19
20
  const fs = require('fs');
20
21
  const path = require('path');
@@ -41,7 +42,7 @@ function generateRCTThirdPartyComponents(
41
42
  'utf8',
42
43
  );
43
44
  const finalPathH = path.join(outputDir, 'RCTThirdPartyComponentsProvider.h');
44
- fs.writeFileSync(finalPathH, templateH);
45
+ writeFileSyncIfChanged(finalPathH, templateH);
45
46
  codegenLog(`Generated artifact: ${finalPathH}`);
46
47
 
47
48
  codegenLog('Generating RCTThirdPartyComponentsProvider.mm');
@@ -150,7 +151,7 @@ function generateRCTThirdPartyComponents(
150
151
  outputDir,
151
152
  'RCTThirdPartyComponentsProvider.mm',
152
153
  );
153
- fs.writeFileSync(finalPathMM, templateMM);
154
+ writeFileSyncIfChanged(finalPathMM, templateMM);
154
155
  codegenLog(`Generated artifact: ${finalPathMM}`);
155
156
  }
156
157
 
@@ -15,7 +15,7 @@ const {
15
15
  TEMPLATES_FOLDER_PATH,
16
16
  packageJson,
17
17
  } = require('./constants');
18
- const {codegenLog} = require('./utils');
18
+ const {codegenLog, writeFileSyncIfChanged} = require('./utils');
19
19
  const {execSync} = require('child_process');
20
20
  const fs = require('fs');
21
21
  const path = require('path');
@@ -39,7 +39,7 @@ function generateReactCodegenPodspec(
39
39
  .replace(/{input-files}/, inputFiles)
40
40
  .replace(/{codegen-script}/, codegenScript);
41
41
  const finalPathPodspec = path.join(outputPath, 'ReactCodegen.podspec');
42
- fs.writeFileSync(finalPathPodspec, finalPodspec);
42
+ writeFileSyncIfChanged(finalPathPodspec, finalPodspec);
43
43
  codegenLog(`Generated podspec: ${finalPathPodspec}`);
44
44
  }
45
45
 
@@ -11,7 +11,7 @@
11
11
  'use strict';
12
12
 
13
13
  const {TEMPLATES_FOLDER_PATH} = require('./constants');
14
- const {parseiOSAnnotations} = require('./utils');
14
+ const {parseiOSAnnotations, writeFileSyncIfChanged} = require('./utils');
15
15
  const fs = require('fs');
16
16
  const path = require('path');
17
17
 
@@ -70,7 +70,7 @@ function generateUnstableModulesRequiringMainQueueSetupProvider(
70
70
 
71
71
  fs.mkdirSync(outputDir, {recursive: true});
72
72
 
73
- fs.writeFileSync(
73
+ writeFileSyncIfChanged(
74
74
  path.join(
75
75
  outputDir,
76
76
  'RCTUnstableModulesRequiringMainQueueSetupProvider.mm',
@@ -82,7 +82,7 @@ function generateUnstableModulesRequiringMainQueueSetupProvider(
82
82
  UNSTABLE_MODULES_REQUIRING_MAIN_QUEUE_SETUP_PROVIDER_H_TEMPLATE_PATH,
83
83
  'utf8',
84
84
  );
85
- fs.writeFileSync(
85
+ writeFileSyncIfChanged(
86
86
  path.join(outputDir, 'RCTUnstableModulesRequiringMainQueueSetupProvider.h'),
87
87
  templateH,
88
88
  );
@@ -485,6 +485,52 @@ function findReactNativeRootPath(projectRoot /* : string */) /* : string */ {
485
485
  return path.dirname(reactNativePackageJsonPath);
486
486
  }
487
487
 
488
+ function writeFileSyncIfChanged(
489
+ targetPath /*: string */,
490
+ contents /*: string */,
491
+ ) {
492
+ try {
493
+ const oldContents = fs.readFileSync(targetPath, 'utf8');
494
+ if (oldContents === contents) {
495
+ return;
496
+ }
497
+ } catch (error) {
498
+ if (error.code !== 'ENOENT') {
499
+ throw error;
500
+ }
501
+ }
502
+ fs.writeFileSync(targetPath, contents);
503
+ }
504
+
505
+ function cpSyncRecursiveIfChanged(
506
+ sourcePath /*: string */,
507
+ targetPath /*: string */,
508
+ ) {
509
+ fs.cpSync(sourcePath, targetPath, {
510
+ recursive: true,
511
+ force: true,
512
+ preserveTimestamps: true,
513
+ filter(src /*: string */, dest /*: string */) {
514
+ try {
515
+ const stat = fs.statSync(src);
516
+ if (!stat.isFile()) {
517
+ return true;
518
+ } else {
519
+ const oldContents = fs.readFileSync(dest, 'utf8');
520
+ const newContents = fs.readFileSync(src, 'utf8');
521
+ return oldContents !== newContents;
522
+ }
523
+ } catch (error) {
524
+ if (error.code !== 'ENOENT' && error.code !== 'EISDIR') {
525
+ throw error;
526
+ } else {
527
+ return true;
528
+ }
529
+ }
530
+ },
531
+ });
532
+ }
533
+
488
534
  module.exports = {
489
535
  buildCodegenIfNeeded,
490
536
  pkgJsonIncludesGeneratedCode,
@@ -499,4 +545,6 @@ module.exports = {
499
545
  readReactNativeConfig,
500
546
  findDisabledLibrariesByPlatform,
501
547
  findReactNativeRootPath,
548
+ writeFileSyncIfChanged,
549
+ cpSyncRecursiveIfChanged,
502
550
  };