@dynatrace/react-native-plugin 2.331.1 → 2.335.1

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 (48) hide show
  1. package/README.md +136 -177
  2. package/android/build.gradle +1 -1
  3. package/android/src/main/java/com/dynatrace/android/agent/DynatraceRNBridgeImpl.kt +7 -1
  4. package/android/src/main/java/com/dynatrace/android/agent/DynatraceUtils.kt +1 -0
  5. package/android/src/new/java/com/dynatrace/android/agent/DynatraceRNBridge.kt +1 -0
  6. package/android/src/old/java/com/dynatrace/android/agent/DynatraceRNBridge.kt +2 -1
  7. package/files/plugin-runtime.gradle +27 -13
  8. package/files/plugin.gradle +1 -1
  9. package/instrumentation/BabelPluginDynatrace.js +1 -0
  10. package/instrumentation/DynatraceInstrumentation.js +1 -1
  11. package/instrumentation/jsx/CreateElement.js +106 -6
  12. package/instrumentation/jsx/JsxDevRuntime.js +2 -6
  13. package/instrumentation/jsx/JsxRuntime.js +6 -10
  14. package/instrumentation/libs/UserInteraction.js +114 -0
  15. package/instrumentation/libs/community/gesture-handler/Touchables.InstrInfo.js +2 -0
  16. package/instrumentation/libs/community/gesture-handler/Touchables.js +3 -1
  17. package/instrumentation/libs/community/gesture-handler/index.js +3 -1
  18. package/instrumentation/libs/withOnPressMonitoring.js +55 -3
  19. package/ios/DynatraceRNBridge.mm +8 -1
  20. package/lib/core/Application.js +2 -0
  21. package/lib/core/Dynatrace.js +2 -1
  22. package/lib/core/UserPrivacyOptions.js +8 -1
  23. package/lib/core/configuration/ConfigurationHandler.js +21 -0
  24. package/lib/dynatrace-reporter.js +0 -14
  25. package/lib/dynatrace-transformer.js +10 -13
  26. package/lib/features/ui-interaction/Config.js +42 -0
  27. package/lib/features/ui-interaction/IUserInteractionEvent.js +16 -0
  28. package/lib/features/ui-interaction/Plugin.Fragment.Test.js +170 -0
  29. package/lib/features/ui-interaction/Plugin.js +289 -0
  30. package/lib/features/ui-interaction/RootDetection.js +51 -0
  31. package/lib/features/ui-interaction/RootWrapper.js +236 -0
  32. package/lib/features/ui-interaction/Run.js +38 -0
  33. package/lib/features/ui-interaction/Runtime.js +827 -0
  34. package/lib/features/ui-interaction/TouchMetaResolver.js +492 -0
  35. package/lib/features/ui-interaction/Types.js +14 -0
  36. package/lib/next/Dynatrace.js +1 -1
  37. package/lib/next/configuration/INativeRuntimeConfiguration.js +1 -0
  38. package/lib/next/configuration/RuntimeConfigurationObserver.js +47 -12
  39. package/lib/next/events/EventPipeline.js +9 -0
  40. package/package.json +19 -13
  41. package/react-native-dynatrace.podspec +1 -1
  42. package/scripts/Android.js +75 -62
  43. package/scripts/Config.js +12 -1
  44. package/scripts/core/InstrumentCall.js +1 -2
  45. package/scripts/core/LineOffsetAnalyzeCall.js +9 -15
  46. package/src/lib/core/interface/NativeDynatraceBridge.ts +1 -0
  47. package/types.d.ts +22 -9
  48. package/scripts/util/ReactOptions.js +0 -21
package/README.md CHANGED
@@ -13,6 +13,8 @@ If you want to start using this plugin and are not a Dynatrace customer yet, hea
13
13
  * User actions for onPress and onLongPress (Touchables, Buttons, Pickers, RefreshControl, Pressable)
14
14
  * User actions for class and functional components (lifecycle events such as render(), didMount() and didUpdate())
15
15
  * Reporting React Native errors
16
+ * Tracking navigation via `react.navigation.enabled`
17
+ * UI Interaction feature toggle via `react.userInteraction` (enable/disable user interaction capturing at runtime)
16
18
  * Manual instrumentation
17
19
  * Typescript bindings to add manual instrumentation
18
20
  * New React-Native architecture
@@ -26,23 +28,23 @@ If you want to start using this plugin and are not a Dynatrace customer yet, hea
26
28
  * Android Gradle plugin version 7.0+
27
29
  * Java 11
28
30
  * Kotlin 2.0.21 - see [Kotlin Compatibility Note](#kotlin-compatibility-note)
29
- * Jetpack Compose 1.4 - 1.9 - see [Compose Compatibility Note](#compose-compatibility-note)
31
+ * Jetpack Compose 1.4 - 1.10 - see [Compose Compatibility Note](#compose-compatibility-note)
30
32
  * For iOS users: Minimum iOS 12
31
33
  * NodeJS 16.0.0+ since our dependencies require NodeJS 16.0.0
32
34
 
33
35
  ## Agent Versions
34
- This agent versions are configured in this plugin:
36
+ These agent versions are configured in this plugin:
35
37
 
36
- * Android Agent: 8.331.1.1004
37
- * iOS Agent: 8.331.1.1008
38
+ * Android Agent: 8.335.1.1001
39
+ * iOS Agent: 8.335.1.1009
38
40
 
39
41
  ## Quick Setup
40
42
 
41
43
  1. [Install plugin](#1-install-the-plugin)
42
- 2. [Register Dynatrace transformer and reporter](#2-register-the-dynatrace-transformer-and-reporter)
43
- 3. [Setup configuration](#3-setup-dynatraceconfigjs)
44
- 4. [Update Babel Configuration](#4-update-babel-configuration)
45
- 5. [Build and run your app](#4-build-and-run-your-app)
44
+ 2. [Setup configuration](#2-setup-dynatraceconfigjs)
45
+ 3. [Register babel plugin](#3-register-our-babel-plugin-in-babelconfigjs)
46
+ 4. [Register jsx-runtime](#4-register-our-jsx-runtime-in-babelconfigjs)
47
+ 5. [Build and run your app](#5-build-and-run-your-app)
46
48
 
47
49
  ## Advanced topics
48
50
  * [Manual OneAgent Startup](#manual-oneagent-startup)
@@ -64,12 +66,14 @@ This agent versions are configured in this plugin:
64
66
  * [Set beacon headers](#setting-beacon-headers)
65
67
  * [Exclude Individual JSX Elements](#exclude-individual-jsx-elements)
66
68
  * [New RUM experience](#new-rum-experience)
69
+ * [User Interaction](#user-interaction)
67
70
  * [Send Event](#send-event)
68
71
  * [Send Session Property Event](#send-session-property-event)
69
72
  * [Event Modifier](#event-modifier)
70
73
  * [Send Exception Event](#send-exception-event)
71
74
  * [Send HTTP Request Event](#send-http-request-event)
72
75
  * [View Monitoring](#view-monitoring)
76
+ * [User Interaction](#user-interaction-1)
73
77
  * [React Native Symbolication](#react-native-symbolication)
74
78
  * [NPX Commands](#npx-commands)
75
79
  * [npx instrumentDynatrace](#npx-instrumentdynatrace)
@@ -89,6 +93,9 @@ This agent versions are configured in this plugin:
89
93
  * [Bundle Name and Version](#bundle-name-and-version)
90
94
  * [Navigation](#navigation)
91
95
  * [Source Map](#source-map)
96
+ * [User Interaction](#user-interaction-1)
97
+ * [Debugging our auto-instrumentation](#debugging-our-auto-instrumentation)
98
+ * [Using our legacy jscodeshift auto-instrumentation](#using-our-legacy-jscodeshift-auto-instrumentation)
92
99
  * [Android block](#android-block)
93
100
  * [iOS block](#ios-block)
94
101
  * [Lifecycle modes](#lifecycle)
@@ -121,9 +128,7 @@ This agent versions are configured in this plugin:
121
128
  > **Note**: If you are upgrading to React Native v0.70 (or newer) or using the @react-native-community/cli 9.x+ version, be aware that our automated script running before every start/run-android/run-ios command is no longer working. When your *dynatrace.config.js* changed be sure to execute `npx instrumentDynatrace` beforehand.
122
129
 
123
130
  ## 1. Install the plugin
124
- 1. Install the plugin by calling:
125
- - React Native v0.60 or newer : `npm install @dynatrace/react-native-plugin`
126
- - React Native v0.59.x : `react-native install @dynatrace/react-native-plugin`.
131
+ 1. Install the plugin by calling `npm install @dynatrace/react-native-plugin`
127
132
  2. **iOS only :** If you use pods, you need to go into your `ios` directory and execute `pod install` to install the new Dynatrace dependency to your xCode project.
128
133
 
129
134
  ### Troubleshooting
@@ -131,81 +136,34 @@ This agent versions are configured in this plugin:
131
136
  - Standalone Project: If you are using React Native standalone and embed it in your native project have a look [here](#configuration-of-standalone-react-native-project).
132
137
  - If for some reason (e.g. seperate native projects) `react-native link` doesn't work as expected, [manually add the iOS agent to your project](#manually-adding-ios-oneagent-to-a-project).
133
138
 
134
- ## 2. Register the Dynatrace transformer and reporter
139
+ ## 2. Setup dynatrace.config.js
135
140
 
136
- The transformer will add modifications to your code during build. The reporter will notify us if you clear the cache of the metro bundler.
137
-
138
- Depending on your React Native version, you will need to use a different way to register the transformer. If you don't know the version, enter `react-native --version` in your terminal.
139
-
140
- The following configuration must be added. If you already have a babel transformer (babelTransformerPath) in place, you need to [use the upstreamTransformer property in dynatrace.config.js](#using-a-second-transformer-besides-the-dynatrace-transformer) to use a transformer besides our dynatrace transformer.
141
-
142
- In your project's root directory, create or extend `metro.config.js` so that it contains the following configuration properties `transformer.babelTransformerPath` and `reporter`:
143
-
144
- #### For React Native v0.72.1 or newer
145
-
146
- ```js
147
- const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
148
- const defaultConfig = getDefaultConfig(__dirname);
149
-
150
- /**
151
- * Metro configuration
152
- * https://facebook.github.io/metro/docs/configuration
153
- *
154
- * @type {import('metro-config').MetroConfig}
155
- */
156
- const config = {
157
- transformer: {
158
- babelTransformerPath: require.resolve(
159
- '@dynatrace/react-native-plugin/lib/dynatrace-transformer',
160
- ),
161
- },
162
- reporter: require('@dynatrace/react-native-plugin/lib/dynatrace-reporter'),
163
- };
164
-
165
- module.exports = mergeConfig(defaultConfig, config);
166
- ```
167
-
168
- #### For Expo
141
+ > **Note**: If you are upgrading from a previous version of this plugin, you'll notice that the file format has changed. Your old configuration is still available in `dynatrace.config` and you have to copy your values to the new `dynatrace.config.js`.
169
142
 
170
- ```js
171
- const {getDefaultConfig} = require('expo/metro-config');
172
- const config = getDefaultConfig(__dirname);
143
+ Define a mobile app in Dynatrace and open the Mobile app instrumentation settings. In the settings you will see a `dynatrace.config.js` file which can be downloaded for React Native. Download and copy this file into the root folder of your application. If you are not sure you can always use `npx configDynatrace` to create a default configuration file.
173
144
 
174
- config.transformer.babelTransformerPath = require.resolve(
175
- '@dynatrace/react-native-plugin/lib/dynatrace-transformer',
176
- );
145
+ > **Note**: Define the components that you want to see lifecycle instrumented ([example](#lifecycle)). This is important as you will only see Application startup and Touches out of the box.
177
146
 
178
- config.reporter = require('@dynatrace/react-native-plugin/lib/dynatrace-reporter');
147
+ For more details about the configuration, see [Advanced topics](#structure-of-the-dynatracejs-file).
179
148
 
180
- module.exports = config;
181
- ```
149
+ ## 3. Register our babel plugin in babel.config.js
182
150
 
183
- #### For React Native v0.59 or newer
151
+ Add the Dynatrace babel plugin to your `babel.config.js`:
184
152
 
185
153
  ```js
186
154
  module.exports = {
187
- transformer: {
188
- babelTransformerPath: require.resolve(
189
- '@dynatrace/react-native-plugin/lib/dynatrace-transformer'
190
- )
191
- },
192
- reporter: require('@dynatrace/react-native-plugin/lib/dynatrace-reporter'),
155
+ ...
156
+ plugins: [..., '@dynatrace/react-native-plugin/instrumentation/BabelPluginDynatrace'],
193
157
  };
194
158
  ```
195
159
 
196
- ## 3. Setup dynatrace.config.js
197
-
198
- > **Note**: If you are upgrading from a previous version of this plugin, you'll notice that the file format has changed. Your old configuration is still available in `dynatrace.config` and you have to copy your values to the new `dynatrace.config.js`.
199
-
200
- Define a mobile app in Dynatrace and open the Mobile app instrumentation settings. In the settings you will see a `dynatrace.config.js` file which can be downloaded for React Native. Download and copy this file into the root folder of your application. If you are not sure you can always use `npx configDynatrace` to create a default configuration file.
160
+ This plugin handles the auto-instrumentation of your React Native code at build time.
201
161
 
202
- > **Note**: Define the components that you want to see lifecycle instrumented ([example](#lifecycle)). This is important as you will only see Application startup and Touches out of the box.
203
-
204
- For more details about the configuration, see [Advanced topics](#structure-of-the-dynatracejs-file).
162
+ > **Note**: We recently moved our auto-instrumentation from a custom metro transformer to a babel plugin. The code shown above is the new and recommended way to add our auto-instrumentation to your project by adding the babel plugin directly. However, our plugin is backwards compatible in a sense that we keep supporting auto-instrumentation via the Dynatrace transformer and reporter. In short, no configuration change is needed. If you continue using the Dynatrace transformer and reporter, we reroute the instrumentation logic to the babel plugin internally.
205
163
 
206
- ## 4. Update Babel Configuration
164
+ ## 4. Register our jsx-runtime in babel.config.js
207
165
 
208
- Depending on your version of Metro or Expo (if used), your babel configuration `babel.config.js` will need to be updated.
166
+ Depending on your version of Metro or Expo (if used), you additionally need to add our jsx-runtime to your babel configuration `babel.config.js`.
209
167
 
210
168
  The changes have to be done in the following cases:
211
169
 
@@ -427,7 +385,7 @@ import { Dynatrace, DynatraceWebRequestTiming } from '@dynatrace/react-native-pl
427
385
  const action = Dynatrace.enterManualAction('API Data Fetch');
428
386
  const url = 'https://api.example.com/data';
429
387
  const tag = await action.getRequestTag(url);
430
- const timing = new DynatraceWebRequestTiming(url, tag);
388
+ const timing = new DynatraceWebRequestTiming(tag, url);
431
389
 
432
390
  try {
433
391
  timing.startWebRequestTiming();
@@ -457,7 +415,7 @@ import { Dynatrace, DynatraceWebRequestTiming } from '@dynatrace/react-native-pl
457
415
  const action = Dynatrace.enterManualAction('API Data Upload');
458
416
  const url = 'https://api.example.com/upload';
459
417
  const tag = await action.getRequestTag(url);
460
- const timing = new DynatraceWebRequestTiming(url, tag);
418
+ const timing = new DynatraceWebRequestTiming(tag, url);
461
419
  const requestData = JSON.stringify({ key: 'value' });
462
420
 
463
421
  try {
@@ -1012,6 +970,42 @@ Dynatrace.sendEvent(new EventData()
1012
970
  Dynatrace.startView("UserProfileDetailed");
1013
971
  ```
1014
972
 
973
+ ### User Interaction
974
+
975
+ User Interaction is an automatic instrumentation feature that captures touch and press events in your React Native application without requiring any manual API calls. When enabled, the plugin instruments your UI components at build time and sends structured interaction events to Dynatrace at runtime.
976
+
977
+ To enable this feature, see the [User Interaction configuration](#user-interaction-1) section.
978
+
979
+ #### Produced data
980
+
981
+ Each captured interaction produces an event describing what the user touched and where. The event includes:
982
+
983
+ - **Element name** — the resolved label of the touched element, derived from its visible text, accessibility label, component name, or test ID.
984
+ - **Component type** — the type of the UI component that was touched (e.g. `Pressable`).
985
+ - **Element path** — a stable path through the component tree that uniquely identifies the element (e.g. `App/View[1]/Pressable[1]`).
986
+ - **Interaction type** — how the user interacted (e.g. `touch`).
987
+ - **Position** — the screen coordinates where the touch occurred.
988
+
989
+ In some cases, a **responder** is also included. The responder is the component that ultimately handled the user's touch — for example, a `Pressable` that received the press event. It carries the same name, component type, and path information as the touched element, and can differ when a touch is visually on a child element but handled by a parent.
990
+
991
+ #### Example event
992
+
993
+ ```json
994
+ {
995
+ "characteristics.has_user_interaction": true,
996
+ "ui_element.detected_name": "LoginButton",
997
+ "ui_element.components": ["Pressable"],
998
+ "ui_element.id": "App/View[1]/Pressable[1]",
999
+ "ui_element.name_origin": "component",
1000
+ "interaction.type": "touch",
1001
+ "positions": [{ "x": 120, "y": 460 }],
1002
+ "ui_element.responder.detected_name": "Pressable",
1003
+ "ui_element.responder.components": ["Pressable"],
1004
+ "ui_element.responder.id": "App/View[1]/Pressable[1]",
1005
+ "ui_element.responder.name_origin": "component"
1006
+ }
1007
+ ```
1008
+
1015
1009
  ### React Native Symbolication
1016
1010
 
1017
1011
  Dynatrace can automatically symbolicate JavaScript stack traces captured by the plugin using sourcemaps. This allows you to view human-readable file names, line numbers, and column information in your crash reports.
@@ -1031,13 +1025,6 @@ To generate a sourcemap:
1031
1025
  * Then run `npx react-native run-ios --mode Release`, or
1032
1026
  * Build for release in Xcode
1033
1027
 
1034
- #### Accounting for Auto-Instrumentation
1035
-
1036
- Since the plugin auto-instruments your code, sourcemap line numbers may be slightly offset. To ensure accurate symbolication:
1037
-
1038
- * **Android**: Automatic patching is **enabled by default** and patches sourcemaps automatically at the end of every release build (see [Source Map](#source-map) configuration)
1039
- * **iOS**: There is currently **no automation** available. You **must** execute [`npx lineOffsetDynatrace`](#npx-lineoffsetdynatrace) to patch your sourcemap files before uploading them to Dynatrace. Without this step, line numbers will be slightly off depending on instrumentation in iOS
1040
-
1041
1028
  #### Uploading Sourcemaps
1042
1029
 
1043
1030
  Once generated and patched, upload your sourcemaps to Dynatrace. For detailed instructions, see the [symbol file management documentation](https://docs.dynatrace.com/docs/observe/digital-experience/mobile-applications/analyze-and-use/upload-and-manage-symbol-files).
@@ -1068,20 +1055,6 @@ npx configDynatrace [optional: config=...]
1068
1055
 
1069
1056
  * `config=C:\SpecialFolderForDynatrace\dynatrace.config.js`: If you have not got your config file in the root folder of the React Native project but somewhere else.
1070
1057
 
1071
- ## npx lineOffsetDynatrace
1072
-
1073
- Our auto-instrumentation modifies your source code during the build process, which causes line numbers in your sourcemaps to become slightly offset from the original source. This can result in incorrect line numbers when viewing crash reports or stack traces in Dynatrace.
1074
-
1075
- The `npx lineOffsetDynatrace` command patches your sourcemap file with offset information, allowing Dynatrace to accurately map instrumented line numbers back to the original source code. The patching process adds Dynatrace-specific metadata alongside the original sourcemap content without modifying any existing fields, ensuring your sourcemap continues to work as expected with all standard tooling.
1076
-
1077
- **Usage:**
1078
-
1079
- ```
1080
- npx lineOffsetDynatrace sourcemapPath=/path/to/your/sourcemap.map
1081
- ```
1082
-
1083
- * `sourcemapPath=/path/to/your/sourcemap.map`: **(Required)** The path to the sourcemap file that should be patched.
1084
-
1085
1058
  ## Customizing paths for configuration
1086
1059
 
1087
1060
  > **Note:** This feature works directly on run-android, run-ios or start command only for React Native v0.60.1 or newer until v0.69.x.
@@ -1269,6 +1242,47 @@ react: {
1269
1242
 
1270
1243
  This activates the debug mode. You will get more console output during instrumentation and at runtime.
1271
1244
 
1245
+
1246
+ #### User Interaction
1247
+
1248
+ ```js
1249
+ react: {
1250
+ userInteraction: true // set it to true here if you want to enable UI interaction, default value is false
1251
+ }
1252
+ ```
1253
+
1254
+ Enables or disables the UI interaction (user interaction) feature.
1255
+ Set to false to disable capturing of user interactions (e.g., touch/click actions) produced by the React Native UI interaction instrumentation.
1256
+
1257
+ ##### What customers will observe after enabling UI Interaction
1258
+
1259
+ After setting `react.userInteraction: true` and rebuilding with instrumentation, the expected flow is:
1260
+
1261
+ 1. Build-time instrumentation wraps supported UI elements and app root entrypoints.
1262
+ 2. At runtime, touch/press interactions are captured and converted into UI interaction events.
1263
+ 3. Events are processed by the plugin event pipeline and sent to Dynatrace.
1264
+ 4. In Dynatrace, customers can analyze captured user interactions (for example, touch-driven behavior and related UI element context).
1265
+
1266
+ Notes:
1267
+
1268
+ - No extra UI needs to be added in customer screens for standard automatic capture.
1269
+ - If `react.debug` is enabled, additional debug output can appear during instrumentation/runtime.
1270
+
1271
+ ##### Runtime switching (config + remote configuration observer)
1272
+
1273
+ UI Interaction can be controlled by two layers:
1274
+
1275
+ 1. **Build/config layer (`dynatrace.config.js`)**
1276
+ - `react.userInteraction: true|false` controls whether the UI Interaction instrumentation feature is applied.
1277
+
1278
+ 2. **Runtime remote layer (`RuntimeConfigurationObserver`)**
1279
+ - Runtime emission checks the remote flag `touch_interaction_enabled`.
1280
+ - If remote flag is present, it is used as the source of truth.
1281
+ - If remote flag is temporarily unavailable, the plugin falls back to the last known good remote value.
1282
+ - If no remote value was received yet, runtime defaults to enabled behavior.
1283
+
1284
+ In short: local config enables the feature path, while remote configuration can dynamically allow/deny event emission at runtime.
1285
+
1272
1286
  #### Error Handler
1273
1287
 
1274
1288
  ```js
@@ -1338,33 +1352,32 @@ react: {
1338
1352
  }
1339
1353
  ```
1340
1354
 
1341
- #### Source Map
1355
+ #### Debugging our auto-instrumentation
1342
1356
 
1343
- This feature is enabled by default and patches sourcemap files so that any modifications made through Dynatrace will result in correct line numbers. If this feature is not enabled, line numbers of symbolicated stacktraces in Dynatrace might not match with your original source.
1344
-
1345
- The following is an example of how this feature can be configured in your `dynatrace.config.js` file:
1357
+ You can see the changes our auto-instrumentation is making to your code with this flag:
1346
1358
 
1347
1359
  ```js
1348
1360
  react: {
1349
- sourceMap: {
1350
- enabled: true
1351
- }
1361
+ debugBabelPlugin: true
1352
1362
  }
1353
1363
  ```
1354
1364
 
1355
- > **Note:** This automatic patching only works for Android. For iOS, you need to manually call [lineOffsetDynatrace](#npx-lineoffsetdynatrace) after the creation of the sourcemap is finished or before uploading it to Dynatrace.
1365
+ The changes our auto-instrumentation is making now get dumped into the `node_modules/@dynatrace/react-native-plugin/build` folder when building the Javascript bundle. The `*.dtx` files show what your code looks like after only our auto-instrumentation was applied to your code. The `*.dtx.downstream` files show what your code looks like after both our auto-instrumentation and all other babel plugins were applied to your code.
1356
1366
 
1357
- The automatic patching for Android can be customized using the `androidSourcemapLocation` property to specify a custom path to your sourcemap file, resolving from the `android/` directory since that is where the automation gets executed:
1367
+ > **Note:** This feature increases the time it takes to build the Javascript bundle. Only use it for debugging purposes. Deactivate it otherwise, especially when fast build times are important.
1368
+
1369
+ #### Using our legacy jscodeshift auto-instrumentation
1370
+
1371
+ We recently moved our auto-instrumentation to a babel plugin. In case you need to, you can still switch to the old jscodeshift auto-instrumentation with this flag:
1358
1372
 
1359
1373
  ```js
1360
1374
  react: {
1361
- sourceMap: {
1362
- enabled: true,
1363
- androidSourcemapLocation: 'sourcemap.map'
1364
- }
1375
+ useLegacyJscodeshift: true
1365
1376
  }
1366
1377
  ```
1367
1378
 
1379
+ > **Note:** When we moved our auto-instrumentation to a babel plugin, we removed documentation that was only relevant for the jscodeshift auto-instrumentation. Most notably, we removed documentation concerning registering a custom metro transformer and patching sourcemaps. If you use the `useLegacyJscodeshift` flag, please refer to the [legacy documentation (v2.333.1)](https://www.npmjs.com/package/@dynatrace/react-native-plugin/v/2.333.1) for details on custom metro transformers and sourcemap patching.
1380
+
1368
1381
  ### Android block
1369
1382
 
1370
1383
  The Android block is a wrapper for the Android configuration you find in the WebUI (in the Mobile Application Settings). Copy the content into the following block:
@@ -1663,75 +1676,6 @@ module.exports = {
1663
1676
  };
1664
1677
  ```
1665
1678
 
1666
- ## Using a second transformer besides the dynatrace transformer
1667
-
1668
- If you want to register the Dynatrace transformer in your configuration and you already have a transformer in place, change the upstreaming transformer for the Dynatrace transformer.
1669
-
1670
- This can be done via a configuration value in the `dynatrace.config.js`. The following example shows how the configuration might look like for the popular `react-native-svg-transformer`. Be aware that the following example is targeting *React Native v0.72.1* or newer. Be aware if you are using a different second transformer, you need to change `react-native-svg-transformer/react-native` accordingly.
1671
-
1672
- #### dynatrace.config.js
1673
-
1674
- ```js
1675
- // The `...` only indicates that there are other values as well, but we've omitted them in this example.
1676
- module.exports = {
1677
- react : {
1678
- upstreamTransformer: require.resolve('react-native-svg-transformer/react-native'),
1679
- // ...
1680
- },
1681
- // ...
1682
- };
1683
- ```
1684
-
1685
- #### metro.config.js for React Native v0.72.1 or newer
1686
- ```js
1687
- const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
1688
- const defaultConfig = getDefaultConfig(__dirname);
1689
- const {assetExts, sourceExts} = defaultConfig.resolver;
1690
-
1691
- /**
1692
- * Metro configuration
1693
- * https://facebook.github.io/metro/docs/configuration
1694
- *
1695
- * @type {import('metro-config').MetroConfig}
1696
- */
1697
- const config = {
1698
- transformer: {
1699
- babelTransformerPath: require.resolve(
1700
- '@dynatrace/react-native-plugin/lib/dynatrace-transformer',
1701
- ),
1702
- },
1703
- reporter: require('@dynatrace/react-native-plugin/lib/dynatrace-reporter'),
1704
- resolver: {
1705
- assetExts: assetExts.filter((ext) => ext !== 'svg'),
1706
- sourceExts: [...sourceExts, 'cjs', 'svg'],
1707
- },
1708
- };
1709
-
1710
- module.exports = mergeConfig(defaultConfig, config);
1711
- ```
1712
-
1713
- #### metro.config.js for React Native v0.59 or newer
1714
-
1715
- ```js
1716
- const { getDefaultConfig } = require("metro-config");
1717
-
1718
- module.exports = (async () => {
1719
- const {
1720
- resolver: { sourceExts, assetExts }
1721
- } = await getDefaultConfig();
1722
- return {
1723
- transformer: {
1724
- babelTransformerPath: require.resolve('@dynatrace/react-native-plugin/lib/dynatrace-transformer')
1725
- },
1726
- reporter: require('@dynatrace/react-native-plugin/lib/dynatrace-reporter'),
1727
- resolver: {
1728
- assetExts: assetExts.filter((ext) => ext !== "svg"),
1729
- sourceExts: [...sourceExts, "cjs", "svg"]
1730
- }
1731
- };
1732
- })();
1733
- ```
1734
-
1735
1679
  ## Maven Central in top-level gradle file
1736
1680
 
1737
1681
  Because the Dynatrace Android agent now requires the MavenCentral repository, if either `jcenter()` or `mavenCentral()` is not added inside of **ALL** the repositories blocks via the [top-level build.gradle](https://dt-url.net/jm610pso), the build will fail.
@@ -1825,7 +1769,7 @@ In summary, ensure your build fulfills our Kotlin 2.0.21 requirement while simul
1825
1769
 
1826
1770
  ## Compose Compatibility Note
1827
1771
 
1828
- Our Android Agent currently supports **Jetpack Compose 1.4 - 1.9**. If you are using an incompatible version of Jetpack Compose, you may encounter the following error message during build:
1772
+ Our Android Agent currently supports **Jetpack Compose 1.4 - 1.10**. If you are using an incompatible version of Jetpack Compose, you may encounter the following error message during build:
1829
1773
 
1830
1774
  ```
1831
1775
  Could not resolve all dependencies for configuration ':app:debugCompileClasspath'.
@@ -1972,6 +1916,21 @@ If you are struggling with a problem, submit a support ticket to Dynatrace (supp
1972
1916
  <br/><br/>
1973
1917
  ## Changelog
1974
1918
 
1919
+ 2.335.1
1920
+ * Updated Android (8.335.1.1001) & iOS Agent (8.335.1.1009)
1921
+ * Dynatrace Android configuration (`dynatrace.gradle`) is now written directly next to the `build.gradle` file instead of inside `node_modules`. Existing projects with the old path are migrated automatically.
1922
+ * Updated Android Gradle plugin configuration for Gradle 9 compatibility in `plugin-runtime.gradle`.
1923
+ * Moved auto-instrumentation from jscodeshift to a Babel plugin, improvements include:
1924
+ * Significantly faster JS bundle builds
1925
+ * Sourcemaps now natively account for our auto-instrumentation - no patching needed
1926
+ * Added instrumentation for `BorderlessButton` and `BaseButton` from `react-native-gesture-handler`.
1927
+
1928
+ 2.333.1
1929
+ * Jetpack Compose support range extended to 1.4 - 1.10 see [Compose Compatibility Note](#compose-compatibility-note)
1930
+ * Updated Android (8.333.1.1006) & iOS Agent (8.333.1.1005)
1931
+ * Added Configuration flag to enable/disable the [User Interaction feature](#user-interaction-1).
1932
+ * Added [User Interaction feature](#user-interaction) to collect UI interaction data such as touches, providing insights into user behavior
1933
+
1975
1934
  2.331.1
1976
1935
  * Updated Android (8.331.1.1004) & iOS Agent (8.331.1.1008)
1977
1936
 
@@ -72,7 +72,7 @@ repositories {
72
72
  }
73
73
 
74
74
  dependencies {
75
- implementation 'com.dynatrace.agent:agent-android:8.331.1.1004'
75
+ implementation 'com.dynatrace.agent:agent-android:8.335.1.1001'
76
76
  implementation "com.facebook.react:react-native:${safeExtGet('reactNative', '+')}"
77
77
  }
78
78
 
@@ -75,6 +75,7 @@ class DynatraceRNBridgeImpl(
75
75
  }
76
76
 
77
77
  Dynatrace.startup(reactApplicationContext, builder.buildConfiguration())
78
+
78
79
  promise.resolve(true)
79
80
  }
80
81
  }
@@ -383,6 +384,9 @@ class DynatraceRNBridgeImpl(
383
384
  )!!
384
385
  )
385
386
  )
387
+ if (userPrivacyOptions.hasKey("_screenRecordOptedIn")) {
388
+ optionsBuilder.withScreenRecordOptedIn(userPrivacyOptions.getBoolean("_screenRecordOptedIn"))
389
+ }
386
390
  Dynatrace.applyUserPrivacyOptions(
387
391
  optionsBuilder
388
392
  .build()
@@ -396,6 +400,7 @@ class DynatraceRNBridgeImpl(
396
400
  val privacyMap = Arguments.createMap()
397
401
  privacyMap.putString("dataCollectionLevel", options.dataCollectionLevel.name)
398
402
  privacyMap.putBoolean("crashReportingOptedIn", options.isCrashReportingOptedIn)
403
+ privacyMap.putBoolean("screenRecordOptedIn", options.isScreenRecordOptedIn)
399
404
  promise.resolve(privacyMap)
400
405
  }
401
406
  }
@@ -446,4 +451,5 @@ class DynatraceRNBridgeImpl(
446
451
  private fun shouldWorkOnAndroid(platform: String?): Boolean {
447
452
  return platform == null || platform == PLATFORM_ANDROID || platform == ""
448
453
  }
449
- }
454
+
455
+ }
@@ -8,6 +8,7 @@ import com.facebook.react.bridge.ReadableType
8
8
  /**
9
9
  * @author matthias.hochrieser
10
10
  */
11
+
11
12
  internal object DynatraceUtils {
12
13
 
13
14
  private const val TAG = "DynatraceUtils"
@@ -238,4 +238,5 @@ class DynatraceRNBridge(
238
238
  override fun stopViewInternal() {
239
239
  impl.stopViewInternal()
240
240
  }
241
+
241
242
  }
@@ -271,4 +271,5 @@ class DynatraceRNBridge(
271
271
  fun stopViewInternal() {
272
272
  impl.stopViewInternal()
273
273
  }
274
- }
274
+
275
+ }
@@ -1,19 +1,33 @@
1
- import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
1
+ import org.gradle.process.ExecOperations
2
+ import javax.inject.Inject
3
+
4
+ abstract class RunLineOffsetTask extends DefaultTask {
5
+ @Internal
6
+ abstract DirectoryProperty getWorkingDirectory()
7
+
8
+ @Inject
9
+ abstract ExecOperations getExecOperations()
10
+
11
+ @TaskAction
12
+ void run() {
13
+ execOperations.exec {
14
+ workingDir workingDirectory.get().asFile
15
+ if (System.getProperty('os.name').toLowerCase().contains('windows')) {
16
+ commandLine 'cmd', '/c', 'npx', 'lineOffsetDynatrace'
17
+ } else {
18
+ commandLine 'npx', 'lineOffsetDynatrace'
19
+ }
20
+ }
21
+ }
22
+ }
2
23
 
3
24
  android.applicationVariants.all { variant ->
4
25
  if (variant.buildType.name == "release") {
26
+ def lineOffsetTask = tasks.register("runLineOffset${variant.name.capitalize()}", RunLineOffsetTask) {
27
+ workingDirectory = layout.projectDirectory
28
+ }
5
29
  variant.mergeAssetsProvider.configure { task ->
6
- task.doLast {
7
- exec {
8
- workingDir rootDir
9
- if (DefaultNativePlatform.currentOperatingSystem.isWindows()) {
10
- // On windows, npx.cmd may not be a true binary executable and may need to be interpreted by a shell instead of executed direclty
11
- commandLine 'cmd', '/c', 'npx', 'lineOffsetDynatrace'
12
- } else {
13
- commandLine 'npx', 'lineOffsetDynatrace'
14
- }
15
- }
16
- }
30
+ task.dependsOn(lineOffsetTask)
17
31
  }
18
32
  }
19
- }
33
+ }
@@ -1,4 +1,4 @@
1
1
  // TEMPLATE: plugin-gradle.template
2
2
  dependencies {
3
- classpath 'com.dynatrace.tools.android:gradle-plugin:8.331.1.1004'
3
+ classpath 'com.dynatrace.tools.android:gradle-plugin:8.335.1.1001'
4
4
  }
@@ -0,0 +1 @@
1
+ var _templateObject,_templateObject2,_templateObject3,_templateObject4,_templateObject5,_templateObject6,_interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault"),_taggedTemplateLiteral2=_interopRequireDefault(require("@babel/runtime/helpers/taggedTemplateLiteral"));function _createForOfIteratorHelper(e,t){var n,r,a,i,o="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(o)return a=!(r=!0),{s:function(){o=o.call(e)},n:function(){var e=o.next();return r=e.done,e},e:function(e){a=!0,n=e},f:function(){try{r||null==o.return||o.return()}finally{if(a)throw n}}};if(Array.isArray(e)||(o=_unsupportedIterableToArray(e))||t&&e&&"number"==typeof e.length)return o&&(e=o),i=0,{s:t=function(){},n:function(){return i>=e.length?{done:!0}:{done:!1,value:e[i++]}},e:function(e){throw e},f:t};throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _unsupportedIterableToArray(e,t){var n;if(e)return"string"==typeof e?_arrayLikeToArray(e,t):"Map"===(n="Object"===(n={}.toString.call(e).slice(8,-1))&&e.constructor?e.constructor.name:n)||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?_arrayLikeToArray(e,t):void 0}function _arrayLikeToArray(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n<t;n++)r[n]=e[n];return r}Object.defineProperty(exports,"__esModule",{value:!0});var reactOptions,fspath=require("path"),fs=require("fs"),core_1=require("@babel/core"),generator_1=require("@babel/generator"),PathsConstants_1=require("../scripts/PathsConstants"),Config_1=require("../scripts/Config"),GetValuesFromPackage_1=require("../lib/core/util/GetValuesFromPackage"),Types_1=require("./model/Types"),INSTRUMENTATION_LIBS="@dynatrace/react-native-plugin/instrumentation/libs",hasJSX=function(e){var t=!1;return e.traverse({"JSXElement|JSXFragment":function(){t=!0}}),t},instrumentUserInteraction=function(e){e.traverse({"FunctionDeclaration|ObjectMethod":function(e){var e=e.node,t=core_1.types.isFunctionDeclaration(e)?e.id:e.key;core_1.types.isIdentifier(t,{name:"registerComponent"})&&(t="".concat(INSTRUMENTATION_LIBS,"/UserInteraction"),e.body.body.unshift(core_1.template.statement('componentProvider = require("'.concat(t,'").wrapProvider(componentProvider);'))()))}})},instrumentAppRegistry=function(e){e.traverse({"FunctionDeclaration|ObjectMethod":function(e){var e=e.node,t=core_1.types.isFunctionDeclaration(e)?e.id:e.key;core_1.types.isIdentifier(t,{name:"runApplication"})&&e.body.body.unshift(core_1.template.statement(_templateObject=_templateObject||(0,_taggedTemplateLiteral2.default)(['require("@dynatrace/react-native-plugin").ApplicationHandler.startup();']))())}})},instrumentExceptionsManager=function(e){var t,n=null!=(t=null==(t=null==reactOptions?void 0:reactOptions.errorHandler)?void 0:t.reportFatalErrorAsCrash)&&t,r=(null==reactOptions?void 0:reactOptions.autoStart)&&(null==(t=null==reactOptions?void 0:reactOptions.errorHandler)?void 0:t.enabled);e.traverse({FunctionDeclaration:function(e){var t;core_1.types.isIdentifier(e.node.id,{name:"handleException"})&&(t=core_1.template.statement(_templateObject2=_templateObject2||(0,_taggedTemplateLiteral2.default)(['\n setTimeout(() => BODY, require("@dynatrace/react-native-plugin/lib/core/ErrorHandler")\n .reportErrorToDynatrace(e, isFatal, CRASH, AUTO));\n ']))({BODY:core_1.types.cloneNode(e.node.body),CRASH:core_1.types.booleanLiteral(n),AUTO:core_1.types.booleanLiteral(!!r)}),e.node.body.body=[t])}})},instrumentCssInterop=function(e){e.traverse({CallExpression:function(e){core_1.types.isIdentifier(e.node.callee,{name:"require"})&&(e=e.node.arguments[0],core_1.types.isStringLiteral(e))&&("react/jsx-runtime"===e.value?e.value="@dynatrace/react-native-plugin/jsx-runtime":"react/jsx-dev-runtime"===e.value&&(e.value="@dynatrace/react-native-plugin/jsx-dev-runtime"))}})},instrumentNavigation=function(e){e.traverse({VariableDeclarator:function(e){var t;core_1.types.isIdentifier(e.node.id,{name:"getRootState"})&&(t=core_1.template.statement(_templateObject3=_templateObject3||(0,_taggedTemplateLiteral2.default)(["\n require(PATH).monitorNavigation(getRootState);\n "]))({PATH:core_1.types.stringLiteral("".concat(INSTRUMENTATION_LIBS,"/react-navigation/ReactNavigation"))}),e.parentPath.insertAfter(t))}})},instrumentReactCreateElement=function(e){var t=core_1.template.statement(_templateObject4=_templateObject4||(0,_taggedTemplateLiteral2.default)(["\n require(PATH).instrumentCreateElement(module.exports);\n "]))({PATH:core_1.types.stringLiteral("@dynatrace/react-native-plugin/instrumentation/jsx/ElementHelper")});e.pushContainer("body",t)},instrumentComponents=function(e,n){e.traverse({FunctionDeclaration:function(e){var t;hasJSX(e)&&(t=e.node,core_1.types.isIdentifier(t.id))&&null!=(e=e.getStatementParent())&&e.insertAfter(n(t.id.name,Types_1.Types.FunctionalComponent))},ClassDeclaration:function(e){var t;hasJSX(e)&&(t=e.node,core_1.types.isIdentifier(t.id))&&null!=(e=e.getStatementParent())&&e.insertAfter(n(t.id.name,Types_1.Types.ClassComponent))},"FunctionExpression|ArrowFunctionExpression":function(e){var t;hasJSX(e)&&(t=e.parent,void 0!==(t=core_1.types.isVariableDeclarator(t)&&core_1.types.isIdentifier(t.id)?t.id.name:core_1.types.isAssignmentExpression(t)&&core_1.types.isIdentifier(t.left)?t.left.name:void 0))&&null!=(e=e.getStatementParent())&&e.insertAfter(n(t,Types_1.Types.FunctionalComponent))}})},instrumentLifecycle=function(e){instrumentComponents(e,function(e,t){return core_1.template.statement(_templateObject5=_templateObject5||(0,_taggedTemplateLiteral2.default)(["NAME._dtInfo = { type: TYPE, name: 'NAME_STR' }"]))({NAME:core_1.types.identifier(e),TYPE:core_1.types.numericLiteral(t),NAME_STR:core_1.types.stringLiteral(e)})})},instrumentComponentNames=function(e){instrumentComponents(e,function(e){return core_1.template.statement(_templateObject6=_templateObject6||(0,_taggedTemplateLiteral2.default)(["NAME.dtName = 'NAME_STR'"]))({NAME:core_1.types.identifier(e),NAME_STR:core_1.types.stringLiteral(e)})})},instrumentInput=function(e){var l=new Map([["react-native",{proxy:"".concat(INSTRUMENTATION_LIBS,"/react-native/"),components:new Set(["TouchableHighlight","TouchableNativeFeedback","TouchableOpacity","TouchableWithoutFeedback","Button","RefreshControl","Text","Pressable","Switch"])}],["react-native-gesture-handler",{proxy:"".concat(INSTRUMENTATION_LIBS,"/community/gesture-handler/"),components:new Set(["TouchableHighlight","TouchableNativeFeedback","TouchableOpacity","TouchableWithoutFeedback","RectButton","BorderlessButton","BaseButton"])}],["@react-native-picker/picker",{proxy:"".concat(INSTRUMENTATION_LIBS,"/community/Picker"),components:new Set(["Picker","PickerIOS"])}]]);e.traverse({ImportDeclaration:function(e){if(void 0===e.node.processed){var t=e.node.source;if(l.has(t.value)){var r=l.get(t.value),n=e.node.specifiers,a=[],i=[];if(n.forEach(function(e,t){var n;core_1.types.isImportSpecifier(e)&&(n=core_1.types.isIdentifier(e.imported)?e.imported.name:e.imported.value,r.components.has(n)||(a.push(e),i.push(t)))}),a.length!==n.length){var o,s=_createForOfIteratorHelper(i.reverse());try{for(s.s();!(o=s.n()).done;){var c=o.value;n.splice(c,1)}}catch(e){s.e(e)}finally{s.f()}0<a.length&&((t=core_1.types.importDeclaration(a,core_1.types.stringLiteral(t.value))).processed=!0,e.insertBefore(t)),e.node.source=core_1.types.stringLiteral(r.proxy),e.scope.crawl()}}}},CallExpression:function(e){core_1.types.isIdentifier(e.node.callee,{name:"require"})&&(e=e.node.arguments[0],core_1.types.isStringLiteral(e))&&l.has(e.value)&&(e.value=l.get(e.value).proxy)}})},instrumentJsxNames=function(e){e.traverse({JSXOpeningElement:function(e){var t=e.node.name,t=core_1.types.isJSXIdentifier(t)?t.name:core_1.types.isJSXMemberExpression(t)?(0,generator_1.default)(t).code:void 0;t&&e.node.attributes.push(core_1.types.jsxAttribute(core_1.types.jsxIdentifier("dtName"),core_1.types.stringLiteral(t)))}})},instrumentConfigurationPreset=function(e){var t,n=(0,GetValuesFromPackage_1.getHostAppBundleInfo)(PathsConstants_1.default.getPackageJsonFile()),r={getLifecycleUpdate:reactOptions.lifecycle.includeUpdate,getLogLevel:reactOptions.debug?0:1,getBundleName:null!=(t=reactOptions.bundleName)?t:null==n?void 0:n.name,getBundleVersion:null!=(t=reactOptions.bundleVersion)?t:null==n?void 0:n.version,getActionNamePrivacy:reactOptions.input.actionNamePrivacy,isErrorHandlerEnabled:reactOptions.errorHandler.enabled,isReportFatalErrorAsCrash:reactOptions.errorHandler.reportFatalErrorAsCrash,isAutoStartupEnabled:reactOptions.autoStart};e.traverse({ClassMethod:function(e){var t;core_1.types.isIdentifier(e.node.key)&&void 0!==(t=r[e.node.key.name])&&(e=e.node.body.body[0],core_1.types.isReturnStatement(e))&&(e.argument=core_1.types.valueToNode(t))}})};exports.default=function(){return{visitor:{Program:function(e,t){reactOptions=(0,Config_1.readConfigDefault)().react;var n,r,a,i,o,s,c=t.filename;void 0!==c&&(a=function(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return t.some(function(e){return c.includes(fspath.join("node_modules",e)+fspath.sep)})},s=function(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return t.some(function(e){return c.endsWith(e)})},!(i="")===reactOptions.debugBabelPlugin&&(i=(0,generator_1.default)(e.node).code),a("@dynatrace")&&s("ConfigurationPreset.js")&&instrumentConfigurationPreset(e),a("react-native")&&s("AppRegistry.js","AppRegistryImpl.js")&&(reactOptions.autoStart&&instrumentAppRegistry(e),reactOptions.userInteraction)&&instrumentUserInteraction(e),a("react-native")&&s("ExceptionsManager.js")&&instrumentExceptionsManager(e),a("react-native-css-interop")&&s("jsx-runtime.js","jsx-dev-runtime.js")&&instrumentCssInterop(e),reactOptions.navigation.enabled&&a("@react-navigation")&&s("BaseNavigationContainer.js","BaseNavigationContainer.tsx")&&instrumentNavigation(e),a("react")&&s("index.js")&&instrumentReactCreateElement(e),null!=(n=null==(r=reactOptions.lifecycle)?void 0:r.instrument)&&n.call(r,c)&&(!c.includes("node_modules")||a("react-native")&&s("renderApplication.js"))&&hasJSX(e)&&instrumentLifecycle(e),null==(r=null==(n=reactOptions.input)?void 0:n.instrument)||!r.call(n,c)||c.includes("node_modules")&&!a("@react-navigation","react-native-drawer-layout")||instrumentInput(e),reactOptions.userInteraction&&!c.includes("node_modules")&&(instrumentJsxNames(e),instrumentComponentNames(e)),!0===reactOptions.debugBabelPlugin)&&(o=(0,generator_1.default)(e.node).code)!==i&&(s=fspath.join(PathsConstants_1.default.getBuildPath(),fspath.relative(PathsConstants_1.default.getApplicationPath(),c)+".dtx"),fs.mkdirSync(fspath.dirname(s),{recursive:!0}),fs.writeFileSync(s,o),t.set("fileDidChange",!0))}},post:function(e){var t;!0===reactOptions.debugBabelPlugin&&!0===this.get("fileDidChange")&&(t=(0,generator_1.default)(e.ast).code,e=fspath.join(PathsConstants_1.default.getBuildPath(),fspath.relative(PathsConstants_1.default.getApplicationPath(),e.opts.filename)+".dtx.downstream"),fs.mkdirSync(fspath.dirname(e),{recursive:!0}),fs.writeFileSync(e,t))}}};