@dynatrace/react-native-plugin 2.321.1 → 2.323.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 (30) hide show
  1. package/README.md +33 -15
  2. package/android/build.gradle +1 -1
  3. package/files/plugin.gradle +1 -1
  4. package/instrumentation/jsx/CreateElement.js +1 -1
  5. package/instrumentation/jsx/ElementHelper.js +2 -16
  6. package/instrumentation/jsx/IDynatraceProperties.js +0 -7
  7. package/instrumentation/jsx/JsxDevRuntime.js +2 -7
  8. package/instrumentation/jsx/JsxRuntime.js +2 -7
  9. package/instrumentation/jsx/components/ComponentUtil.js +12 -1
  10. package/instrumentation/jsx/components/Picker.js +3 -3
  11. package/instrumentation/jsx/components/RefreshControl.js +3 -3
  12. package/instrumentation/jsx/components/Switch.js +4 -4
  13. package/instrumentation/libs/community/gesture-handler/Touchables.js +7 -37
  14. package/instrumentation/libs/react-native/Touchables.js +8 -47
  15. package/instrumentation/libs/withOnPressMonitoring.js +121 -0
  16. package/instrumentation/model/Types.js +3 -15
  17. package/instrumentation/model/TypesUtil.js +5 -34
  18. package/lib/next/Dynatrace.js +3 -0
  19. package/lib/next/events/spec/EventSpecContstants.js +1 -1
  20. package/package.json +6 -4
  21. package/react-native-dynatrace.podspec +1 -1
  22. package/scripts/Ios.js +1 -1
  23. package/scripts/Logger.js +20 -1
  24. package/scripts/core/InstrumentCall.js +62 -34
  25. package/scripts/core/LineOffsetAnalyzeCall.js +18 -15
  26. package/scripts/util/ReactOptions.js +21 -0
  27. package/scripts/util/SourceMapUtil.js +4 -4
  28. package/src/instrumentation/jsx/IDynatraceProperties.ts +15 -0
  29. package/typings/react-native-dynatrace.d.ts +9 -0
  30. package/instrumentation/jsx/components/Touchable.js +0 -151
@@ -44,6 +44,7 @@ class DynatraceImpl {
44
44
  if (isApiReported) {
45
45
  event["characteristics.is_api_reported"] =
46
46
  true;
47
+ event["dt.support.is_legacy_api_reported"] = true;
47
48
  }
48
49
  EventPipeline_1.EventPipeline.insertEvent(event);
49
50
  }
@@ -54,6 +55,7 @@ class DynatraceImpl {
54
55
  if (isApiReported) {
55
56
  event["characteristics.is_api_reported"] =
56
57
  true;
58
+ event["dt.support.is_legacy_api_reported"] = true;
57
59
  }
58
60
  EventPipeline_1.EventPipeline.insertEvent(event);
59
61
  }
@@ -64,6 +66,7 @@ class DynatraceImpl {
64
66
  if (isApiReported) {
65
67
  event["characteristics.is_api_reported"] =
66
68
  true;
69
+ event["dt.support.is_legacy_api_reported"] = true;
67
70
  }
68
71
  EventPipeline_1.EventPipeline.insertEvent(event);
69
72
  }
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ALL_APP_START_KEYS = exports.MODIFY_EVENT_WHITELIST_NAMESPACE = exports.MODIFY_EVENT_WHITELIST_FIELDS = exports.SEND_SESSION_PROPERTY_EVENT_WHITELIST_FIELDS = exports.SEND_EVENT_WHITELIST_FIELDS = exports.SEND_SESSION_PROPERTY_EVENT_WHITELIST_NAMESPACES = exports.SEND_EVENT_WHITELIST_NAMESPACES = exports.AllCharacteristicsKeys = exports.KEY_NAME_REGEX = exports.MAX_CUSTOM_EVENT_VALUE_LENGTH = exports.MAX_CUSTOM_EVENT_KEY_LENGTH = exports.MAX_CUSTOM_EVENT_FIELDS = void 0;
4
- const SPECIFICATION_VERSION = '0.20';
4
+ const SPECIFICATION_VERSION = '0.21';
5
5
  exports.MAX_CUSTOM_EVENT_FIELDS = 50;
6
6
  exports.MAX_CUSTOM_EVENT_KEY_LENGTH = 100;
7
7
  exports.MAX_CUSTOM_EVENT_VALUE_LENGTH = 5000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynatrace/react-native-plugin",
3
- "version": "2.321.1",
3
+ "version": "2.323.1",
4
4
  "description": "This plugin gives you the ability to use the Dynatrace Mobile agent in your react native application.",
5
5
  "main": "index.js",
6
6
  "types": "typings/react-native-dynatrace.d.ts",
@@ -46,7 +46,7 @@
46
46
  "author": "Dynatrace",
47
47
  "license": "SEE LICENSE IN LICENSE.md",
48
48
  "dependencies": {
49
- "@babel/runtime": "^7.28.3",
49
+ "@babel/runtime": "^7.28.4",
50
50
  "jscodeshift": "^17.3.0",
51
51
  "plist": "^3.1.0",
52
52
  "proxy-polyfill": "^0.3.2",
@@ -78,7 +78,7 @@
78
78
  "@testing-library/react-native": "^13.2.0",
79
79
  "@types/jest": "^30.0.0",
80
80
  "@types/jscodeshift": "^17.3.0",
81
- "@types/node": "^18.19.71",
81
+ "@types/node": "^18.19.123",
82
82
  "@types/plist": "^3.0.5",
83
83
  "@types/react": "^19.1.8",
84
84
  "@types/semver": "^7.7.0",
@@ -101,6 +101,7 @@
101
101
  "jest-mock": "^30.0.2",
102
102
  "npm-check-updates": "^18.0.1",
103
103
  "prettier": "^2.6.1",
104
+ "react-native-gesture-handler": "^2.28.0",
104
105
  "shelljs": "^0.10.0",
105
106
  "ts-jest": "^29.4.0",
106
107
  "typescript": "^4.7.4",
@@ -154,7 +155,8 @@
154
155
  "lib/next/events/modifier/*.js",
155
156
  "lib/next/events/spec/*.js",
156
157
  "lib/next/provider/*.js",
157
- "src/lib/core/interface/NativeDynatraceBridge.ts"
158
+ "src/lib/core/interface/NativeDynatraceBridge.ts",
159
+ "src/instrumentation/jsx/IDynatraceProperties.ts"
158
160
  ],
159
161
  "codegenConfig": {
160
162
  "name": "DynatraceBridgeSpec",
@@ -111,7 +111,7 @@ Pod::Spec.new do |s|
111
111
  #
112
112
 
113
113
  s.dependency "React"
114
- s.dependency 'Dynatrace', '~> 8.321.1.1007'
114
+ s.dependency 'Dynatrace', '~> 8.323.1.1009'
115
115
 
116
116
  # Allows for better compatibility for older and newer versions
117
117
  if defined?(install_modules_dependencies)
package/scripts/Ios.js CHANGED
@@ -186,7 +186,7 @@ const createNewPListIfRequired = (parsedPList, configProps, pathToPList) => {
186
186
  const configIncludingFlavor = configProps + getAdditionalInternalPluginKeys() + updatedExcludedStr(configProps);
187
187
  if (isPropertyCountEqual(parsedPList, configIncludingFlavor) && comparePListAndConfig(parsedPList, configIncludingFlavor)) {
188
188
  Logger_1.default.logMessageSync('Not generating a new plist as the current plist and ' +
189
- ' dynatrace.config.js iOS properties are identical!', Logger_1.default.INFO);
189
+ 'dynatrace.config.js iOS properties are identical!', Logger_1.default.INFO);
190
190
  }
191
191
  else {
192
192
  Logger_1.default.logMessageSync('Generating a new plist as the current plist ' +
package/scripts/Logger.js CHANGED
@@ -41,19 +41,23 @@ const logMessageSync = (_message, _logLevel, _onlyConsole = false) => {
41
41
  catch (e) {
42
42
  }
43
43
  let logString;
44
+ let prefix = '';
44
45
  if (_logLevel === INFO) {
45
46
  logString = '#INFO ';
47
+ prefix = _prefix.info;
46
48
  }
47
49
  else if (_logLevel === WARNING) {
48
50
  logString = '#WARN ';
51
+ prefix = _prefix.warning;
49
52
  }
50
53
  else if (_logLevel === ERROR) {
51
54
  logString = '#ERROR ';
55
+ prefix = _prefix.error;
52
56
  }
53
57
  else {
54
58
  logString = '#NONE ';
55
59
  }
56
- const outputString = logString + '[' + currentDate() + ']: ' + _message;
60
+ const outputString = logString + '[' + currentDate() + ']: ' + prefix + _message;
57
61
  console.log(outputString);
58
62
  if (!_onlyConsole) {
59
63
  fs.appendFileSync(PathsConstants_1.default.getCurrentLogPath(), outputString + '\r\n');
@@ -67,10 +71,25 @@ const currentDate = () => {
67
71
  const localISOTime = (new Date(Date.now() - tzoffset)).toISOString().slice(0, -5);
68
72
  return localISOTime.replace('T', ' ');
69
73
  };
74
+ const noPrefix = {
75
+ info: '',
76
+ warning: '',
77
+ error: '',
78
+ };
79
+ let _prefix = noPrefix;
70
80
  exports.default = {
71
81
  ERROR,
72
82
  INFO,
73
83
  WARNING,
84
+ withPrefix(prefix, callback) {
85
+ try {
86
+ _prefix = Object.assign(Object.assign({}, _prefix), prefix);
87
+ return callback();
88
+ }
89
+ finally {
90
+ _prefix = noPrefix;
91
+ }
92
+ },
74
93
  closeLogFile,
75
94
  logMessageSync,
76
95
  logErrorSync,
@@ -6,14 +6,42 @@ const Logger_1 = require("../Logger");
6
6
  const Config_1 = require("../Config");
7
7
  const android = require("../Android");
8
8
  const PathsConstants_1 = require("../PathsConstants");
9
+ const nodePath = require("path");
9
10
  const Ios_1 = require("../Ios");
10
11
  const InstrumentUtil_1 = require("../util/InstrumentUtil");
11
12
  const CustomArgumentUtil_1 = require("../util/CustomArgumentUtil");
12
13
  const Platform_1 = require("../api/model/Platform");
13
14
  const SourceMapUtil_1 = require("../util/SourceMapUtil");
14
15
  const instrumentCommand = () => {
15
- Logger_1.default.logMessageSync('Starting instrumentation of React Native application ..', Logger_1.default.INFO);
16
- (0, InstrumentUtil_1.showVersionOfPlugin)();
16
+ if (process.argv.includes('--help')) {
17
+ Logger_1.default.logMessageSync(`
18
+ Usage: npx instrumentDynatrace [options]
19
+
20
+ Options:
21
+ --help Show this help message and exit.
22
+ config="/custom/path/to/dynatrace.config.js"
23
+ Specify a custom path to the Dynatrace configuration file.
24
+ Default: ${PathsConstants_1.default.getConfigFilePath()}
25
+ gradle="/custom/path/to/build.gradle(.kts)"
26
+ Specify a custom path to the top level build.gradle file of your android project.
27
+ Default: ${PathsConstants_1.default.getAndroidGradleFile(PathsConstants_1.default.getAndroidFolder())}
28
+ plist="/custom/path/to/Info.plist"
29
+ Specify a custom path to the Info.plist file of your ios project.
30
+ Default: Extracts <appName> from app.json and then tries
31
+ ${nodePath.join(PathsConstants_1.default.getIOSFolder(), "<appName>", 'Info.plist')}
32
+ and ${nodePath.join(PathsConstants_1.default.getIOSFolder(), "<appName>", "Supporting", "Info.plist")}
33
+ in that order
34
+
35
+ Examples:
36
+ npx instrumentDynatrace
37
+ npx instrumentDynatrace config="/custom/path/to/dynatrace.config.js"
38
+ npx instrumentDynatrace gradle="/custom/path/to/build.gradle"
39
+ npx instrumentDynatrace plist="/custom/path/to/Info.plist"
40
+ `, Logger_1.default.INFO);
41
+ return;
42
+ }
43
+ Logger_1.default.logMessageSync('⏳ Starting instrumentation of React Native application ..', Logger_1.default.INFO);
44
+ Logger_1.default.withPrefix({ info: ' ℹ️ ', warning: ' ⚠️ ' }, () => (0, InstrumentUtil_1.showVersionOfPlugin)());
17
45
  let pathToConfig = PathsConstants_1.default.getConfigFilePath();
18
46
  let pathToGradle = PathsConstants_1.default.getAndroidGradleFile(PathsConstants_1.default.getAndroidFolder());
19
47
  const pathToAppGradle = PathsConstants_1.default.getAndroidAppGradleFile(PathsConstants_1.default.getAndroidFolder());
@@ -30,69 +58,69 @@ const instrumentCommand = () => {
30
58
  if (argv.isCustomConfigurationPathSet()) {
31
59
  pathToConfig = argv.getCustomConfigurationPath();
32
60
  }
33
- if (argv.isCustomGradlePathSet()) {
34
- pathToGradle = argv.getCustomGradlePath();
35
- androidAvailable = (0, InstrumentUtil_1.isPlatformAvailable)(pathToGradle, Platform_1.Platform.Android);
36
- }
37
- else {
38
- androidAvailable = (0, InstrumentUtil_1.isPlatformAvailable)(PathsConstants_1.default.getAndroidFolder(), Platform_1.Platform.Android);
39
- }
40
- if (argv.isCustomPlistPathSet()) {
41
- pathToPList = (0, path_1.resolve)(argv.getCustomPlistPath());
42
- iosAvailable = (0, InstrumentUtil_1.isPlatformAvailable)(pathToPList, Platform_1.Platform.IOS);
43
- }
44
- else {
45
- iosAvailable = (0, InstrumentUtil_1.isPlatformAvailable)(PathsConstants_1.default.getIOSFolder(), Platform_1.Platform.IOS);
46
- }
61
+ Logger_1.default.withPrefix({ info: ' ℹ️ ', warning: ' ⚠️ ', error: ' ❌ ' }, () => {
62
+ if (argv.isCustomGradlePathSet()) {
63
+ pathToGradle = argv.getCustomGradlePath();
64
+ androidAvailable = (0, InstrumentUtil_1.isPlatformAvailable)(pathToGradle, Platform_1.Platform.Android);
65
+ }
66
+ else {
67
+ androidAvailable = (0, InstrumentUtil_1.isPlatformAvailable)(PathsConstants_1.default.getAndroidFolder(), Platform_1.Platform.Android);
68
+ }
69
+ if (argv.isCustomPlistPathSet()) {
70
+ pathToPList = (0, path_1.resolve)(argv.getCustomPlistPath());
71
+ iosAvailable = (0, InstrumentUtil_1.isPlatformAvailable)(pathToPList, Platform_1.Platform.IOS);
72
+ }
73
+ else {
74
+ iosAvailable = (0, InstrumentUtil_1.isPlatformAvailable)(PathsConstants_1.default.getIOSFolder(), Platform_1.Platform.IOS);
75
+ }
76
+ });
47
77
  pathToConfig = (0, path_1.resolve)(pathToConfig);
48
78
  pathToGradle = (0, path_1.resolve)(pathToGradle);
49
79
  if (iosAvailable || androidAvailable) {
50
80
  try {
51
- Logger_1.default.logMessageSync('Trying to read configuration file: ' + pathToConfig, Logger_1.default.INFO);
81
+ Logger_1.default.logMessageSync('Trying to read configuration file: ' + pathToConfig, Logger_1.default.INFO);
52
82
  const configAgent = (0, Config_1.readConfig)(pathToConfig);
53
83
  if (androidAvailable) {
54
84
  try {
55
- Logger_1.default.logMessageSync('Starting Android Instrumentation with Dynatrace!', Logger_1.default.INFO);
56
- android.instrumentAndroidPlatform(pathToGradle, false);
57
- android.writeGradleConfig(configAgent.android);
58
- android.ensureRuntimeScriptApplied(pathToAppGradle);
85
+ Logger_1.default.logMessageSync('Starting Android Instrumentation with Dynatrace!', Logger_1.default.INFO);
86
+ Logger_1.default.withPrefix({ info: ' ℹ️ ', warning: ' ⚠️ ', error: ' ❌ ' }, () => {
87
+ android.instrumentAndroidPlatform(pathToGradle, false);
88
+ android.writeGradleConfig(configAgent.android);
89
+ android.ensureRuntimeScriptApplied(pathToAppGradle);
90
+ });
59
91
  android.copyGradleConfigFile(pathToGradle);
92
+ Logger_1.default.logMessageSync(' ✅ Finished Android Instrumentation with Dynatrace!', Logger_1.default.INFO);
60
93
  }
61
94
  catch (e) {
62
95
  if (e instanceof Error) {
63
- Logger_1.default.logMessageSync(e.message, Logger_1.default.ERROR);
96
+ Logger_1.default.logMessageSync(' ❌ Android Instrumentation failed: ' + e.message, Logger_1.default.ERROR);
64
97
  }
65
98
  }
66
- finally {
67
- Logger_1.default.logMessageSync('Finished Android Instrumentation with Dynatrace!', Logger_1.default.INFO);
68
- }
69
99
  }
70
100
  if (iosAvailable) {
71
101
  try {
72
- Logger_1.default.logMessageSync('Starting iOS Instrumentation with Dynatrace!', Logger_1.default.INFO);
73
- Ios_1.default.modifyPListFile(pathToPList, configAgent.ios, false);
102
+ Logger_1.default.logMessageSync('Starting iOS Instrumentation with Dynatrace!', Logger_1.default.INFO);
103
+ Logger_1.default.withPrefix({ info: ' ℹ️ ', warning: ' ⚠️ ', error: ' ❌ ' }, () => Ios_1.default.modifyPListFile(pathToPList, configAgent.ios, false));
104
+ Logger_1.default.logMessageSync(' ✅ Finished iOS Instrumentation with Dynatrace!', Logger_1.default.INFO);
74
105
  }
75
106
  catch (e) {
76
107
  if (e instanceof Error) {
77
- Logger_1.default.logMessageSync(e.message, Logger_1.default.ERROR);
108
+ Logger_1.default.logMessageSync(' ❌ iOS Instrumentation failed: ' + e.message, Logger_1.default.ERROR);
78
109
  }
79
110
  }
80
- finally {
81
- Logger_1.default.logMessageSync('Finished iOS Instrumentation with Dynatrace!', Logger_1.default.INFO);
82
- }
83
111
  }
84
112
  (0, SourceMapUtil_1.patchMetroSourceMap)();
113
+ Logger_1.default.logMessageSync('🎉 Finished instrumentation of React Native application ..', Logger_1.default.INFO);
85
114
  }
86
115
  catch (e) {
87
116
  if (e instanceof Error) {
88
- Logger_1.default.logMessageSync(e.message, Logger_1.default.ERROR);
117
+ Logger_1.default.logMessageSync('❌ Instrumentation failed: ' + e.message, Logger_1.default.ERROR);
89
118
  }
90
119
  }
91
120
  }
92
121
  else {
93
- Logger_1.default.logMessageSync('Both Android and iOS Folder are not available - Skip instrumentation.', Logger_1.default.WARNING);
122
+ Logger_1.default.logMessageSync('⚠️ Both Android and iOS Folder are not available - Skip instrumentation.', Logger_1.default.WARNING);
94
123
  }
95
- Logger_1.default.logMessageSync('Finished instrumentation of React Native application ..', Logger_1.default.INFO);
96
124
  Logger_1.default.closeLogFile();
97
125
  };
98
126
  exports.instrumentCommand = instrumentCommand;
@@ -16,6 +16,7 @@ const diff_1 = require("diff");
16
16
  const Logger_1 = require("../Logger");
17
17
  const InstrumentUtil_1 = require("../util/InstrumentUtil");
18
18
  const FileOperationHelper_1 = require("../../scripts/FileOperationHelper");
19
+ const ReactOptions_1 = require("../util/ReactOptions");
19
20
  class LineOffsetAnalyzer {
20
21
  constructor(options) {
21
22
  this.rootDir = (0, path_1.resolve)(options.projectRoot);
@@ -32,26 +33,26 @@ class LineOffsetAnalyzer {
32
33
  try {
33
34
  const buildExists = yield FileOperationHelper_1.default.checkIfFileExists(this.instrumentedDir);
34
35
  if (!buildExists) {
35
- yield this.log(`❌ Build directory not found at: ${this.instrumentedDir}`);
36
- yield this.log('🛠️ Please run the instrumentation step first!');
37
- console.error('🚫 Build directory missing. Make and instrument the project first.');
36
+ yield this.log(`❌ Build directory not found at: ${this.instrumentedDir}`, true);
37
+ yield this.log('🛠️ Please run the instrumentation step first!', true);
38
+ console.error('🚫 Build directory missing. Make and instrument the project first.', true);
38
39
  process.exit(1);
39
40
  }
40
41
  yield fs.writeFile(this.logFile, '');
41
42
  const instrumentedFiles = (yield FileOperationHelper_1.default.getAllFiles(this.log, this.instrumentedDir))
42
43
  .filter((filePath) => filePath.endsWith(InstrumentUtil_1.INSTRUMENTED_FILE_EXTENSION));
43
44
  const mappings = {};
44
- yield this.log(`🔍 Found ${instrumentedFiles.length} instrumented files`);
45
+ yield this.log(`🔍 Found ${instrumentedFiles.length} instrumented files`, true);
45
46
  for (const instrRelPath of instrumentedFiles) {
46
47
  const baseRelPath = instrRelPath.slice(0, -InstrumentUtil_1.INSTRUMENTED_FILE_EXTENSION.length);
47
48
  const origPath = (0, path_1.join)(this.rootDir, baseRelPath);
48
49
  const instrPath = (0, path_1.join)(this.instrumentedDir, instrRelPath);
49
50
  const origExists = yield FileOperationHelper_1.default.checkIfFileExists(origPath);
50
51
  if (!origExists) {
51
- yield this.log(`⚠️ Original file not found for: ${baseRelPath}`);
52
+ yield this.log(`⚠️ Original file not found for: ${baseRelPath}`, true);
52
53
  continue;
53
54
  }
54
- yield this.log(`📄 Comparing: ${baseRelPath}`);
55
+ yield this.log(`📄 Comparing: ${baseRelPath}`, true);
55
56
  const [originalContent, instrumentedContent] = yield Promise.all([
56
57
  FileOperationHelper_1.default.readTextFromFile(origPath),
57
58
  FileOperationHelper_1.default.readTextFromFile(instrPath),
@@ -59,10 +60,10 @@ class LineOffsetAnalyzer {
59
60
  const offsets = this.computeLineOffsets(originalContent, instrumentedContent);
60
61
  if (offsets.length > 0) {
61
62
  mappings[baseRelPath] = offsets;
62
- yield this.log(`✅ Offsets found for ${baseRelPath}: ${offsets.length} entries`);
63
+ yield this.log(`✅ Offsets found for ${baseRelPath}: ${offsets.length} entries`, true);
63
64
  }
64
65
  else {
65
- yield this.log(`ℹ️ No offsets needed for ${baseRelPath}`);
66
+ yield this.log(`ℹ️ No offsets needed for ${baseRelPath}`, true);
66
67
  }
67
68
  }
68
69
  const finalOutput = {
@@ -74,7 +75,7 @@ class LineOffsetAnalyzer {
74
75
  },
75
76
  };
76
77
  yield fs.writeFile(this.outputFile, JSON.stringify(finalOutput, null, 2));
77
- yield this.log(`✅ Line offsets written to ${this.outputFile}`);
78
+ yield this.log(`✅ Line offsets written to ${this.outputFile}`, true);
78
79
  if (this.isProd) {
79
80
  yield this.log('🔧 Patching source map with all offset data...');
80
81
  const loadedMap = yield this.loadSourceMap();
@@ -117,22 +118,24 @@ class LineOffsetAnalyzer {
117
118
  foundAny = true;
118
119
  const content = yield fs.readFile(mapPath, 'utf-8');
119
120
  const parsed = JSON.parse(content);
120
- yield this.log(`🗺️ Found and parsed source map at: ${mapPath}`);
121
+ yield this.log(`🗺️ Found and parsed source map at: ${mapPath}`, true);
121
122
  return { map: parsed, path: mapPath };
122
123
  }
123
124
  catch (err) {
124
- yield this.log(`⚠️ Failed to load or parse source map at ${mapPath}: ${err.message}`);
125
+ yield this.log(`⚠️ Failed to load or parse source map at ${mapPath}: ${err.message}`, true);
125
126
  }
126
127
  }
127
128
  if (!foundAny) {
128
- yield this.log('❌ No valid source map found in any of the expected locations.');
129
+ yield this.log('❌ No valid source map found in any of the expected locations.', true);
129
130
  }
130
131
  return null;
131
132
  });
132
133
  }
133
- log(msg) {
134
+ log(msg, logOnScreen = false) {
134
135
  return __awaiter(this, void 0, void 0, function* () {
135
- Logger_1.default.logMessageSync(msg, Logger_1.default.INFO);
136
+ if (ReactOptions_1.reactOptions && ReactOptions_1.reactOptions.react.debug && logOnScreen) {
137
+ Logger_1.default.logMessageSync(msg, Logger_1.default.INFO);
138
+ }
136
139
  yield fs.appendFile(this.logFile, msg + '\n');
137
140
  });
138
141
  }
@@ -143,7 +146,7 @@ class LineOffsetAnalyzer {
143
146
  let offset = 0;
144
147
  let lastRecordedOffset = null;
145
148
  const offsets = [];
146
- this.log('🔍 Diff result:');
149
+ this.log('🔍 Diff result:', true);
147
150
  for (const part of diffs) {
148
151
  const lines = part.value.split('\n').slice(0, -1);
149
152
  if (part.added) {
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.reactOptions = void 0;
4
+ const nodePath = require("path");
5
+ const PathsConstants_1 = require("../../scripts/PathsConstants");
6
+ const CustomArgumentUtil_1 = require("../util/CustomArgumentUtil");
7
+ const config = require("../../scripts/Config");
8
+ const customArguments = (0, CustomArgumentUtil_1.readCustomArguments)();
9
+ let reactOptions;
10
+ exports.reactOptions = reactOptions;
11
+ try {
12
+ if (customArguments.isCustomConfigurationPathSet()) {
13
+ exports.reactOptions = reactOptions = config.readConfig(nodePath.join(PathsConstants_1.default.getApplicationPath(), customArguments.getCustomConfigurationPath()));
14
+ }
15
+ else {
16
+ exports.reactOptions = reactOptions = config.readConfig(nodePath.join(PathsConstants_1.default.getConfigFilePath()));
17
+ }
18
+ }
19
+ catch (_a) {
20
+ exports.reactOptions = reactOptions = undefined;
21
+ }
@@ -8,11 +8,11 @@ const FileOperationHelper_1 = require("../FileOperationHelper");
8
8
  exports.SOURCE_MAP_BACKUP_FILE = 'getSourceMapInfoOrig.js';
9
9
  exports.SOURCE_MAP_FILE = 'getSourceMapInfo.js';
10
10
  const patchMetroSourceMap = () => {
11
- Logger_1.default.logMessageSync('Patching SourceMap generation of Metro .. ', Logger_1.default.INFO);
11
+ Logger_1.default.logMessageSync('Patching SourceMap generation of Metro .. ', Logger_1.default.INFO);
12
12
  const origSourceMapPath = (0, path_1.join)(PathsConstants_1.default.getMetroSouceMapPath(), exports.SOURCE_MAP_BACKUP_FILE);
13
13
  try {
14
14
  FileOperationHelper_1.default.checkIfFileExistsSync(origSourceMapPath);
15
- Logger_1.default.logMessageSync('Patching of SourceMap already happened!', Logger_1.default.INFO);
15
+ Logger_1.default.logMessageSync(' ℹ️ Patching of SourceMap already happened!', Logger_1.default.INFO);
16
16
  }
17
17
  catch (e) {
18
18
  try {
@@ -20,10 +20,10 @@ const patchMetroSourceMap = () => {
20
20
  FileOperationHelper_1.default.checkIfFileExistsSync(currentSourceMapPath);
21
21
  FileOperationHelper_1.default.renameFileSync(currentSourceMapPath, origSourceMapPath);
22
22
  FileOperationHelper_1.default.copyFileSync(PathsConstants_1.default.getOurSourceMapFile(), currentSourceMapPath);
23
- Logger_1.default.logMessageSync('Patching of SourceMap successful!', Logger_1.default.INFO);
23
+ Logger_1.default.logMessageSync('Patching of SourceMap successful!', Logger_1.default.INFO);
24
24
  }
25
25
  catch (e) {
26
- Logger_1.default.logMessageSync('Patching of SourceMap generation failed!', Logger_1.default.ERROR);
26
+ Logger_1.default.logMessageSync('Patching of SourceMap generation failed!', Logger_1.default.ERROR);
27
27
  }
28
28
  }
29
29
  };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Interface which is containing the additional properties available for instrumentation.
3
+ */
4
+ export interface IDynatraceProperties {
5
+ /**
6
+ * This string is changing the name of the action. So it is possible to override the action naming.
7
+ */
8
+ dtActionName?: string;
9
+
10
+ /**
11
+ * If true is passed the auto instrumentation will skip the action creation. We allow both string
12
+ * and boolean.
13
+ */
14
+ dtActionIgnore?: boolean | 'true' | 'false';
15
+ }
@@ -1,3 +1,6 @@
1
+ import 'react'
2
+ import { IDynatraceProperties } from '../src/instrumentation/jsx/IDynatraceProperties';
3
+
1
4
  /**
2
5
  * react-native-dynatrace.d.ts
3
6
  *
@@ -1717,4 +1720,10 @@ export declare class ConfigurationBuilder {
1717
1720
  * @see https://www.npmjs.com/package/@dynatrace/react-native-plugin#plugin-startup
1718
1721
  */
1719
1722
  public buildConfiguration(): IConfiguration;
1723
+ }
1724
+
1725
+ declare module 'react' {
1726
+ namespace JSX {
1727
+ interface IntrinsicAttributes extends IDynatraceProperties {}
1728
+ }
1720
1729
  }
@@ -1,151 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TouchableHelper = void 0;
4
- const ConfigurationHandler_1 = require("../../../lib/core/configuration/ConfigurationHandler");
5
- const IDynatraceProperties_1 = require("../IDynatraceProperties");
6
- const TypesUtil_1 = require("../../model/TypesUtil");
7
- const TouchableHelper = (Dynatrace, Logger) => ({
8
- attachOnPress(longPress, props, children, type) {
9
- const origFunction = longPress && this._isLongPress(props)
10
- ? props.onLongPress
11
- : props.onPress;
12
- const nameOfAction = this._findActionName(props, children);
13
- const wrappedFunction = (event) => {
14
- if (nameOfAction == null) {
15
- Logger.debug('Skipping creation of action as no name was found!');
16
- if (origFunction != null) {
17
- return origFunction(event);
18
- }
19
- }
20
- else if (origFunction != null &&
21
- origFunction._dtWrapped !== undefined &&
22
- origFunction._dtWrapped === true) {
23
- Logger.debug(`Skip wrapping of ${nameOfAction} onPress as it is already wrapped!`);
24
- return origFunction(event);
25
- }
26
- else if (!ConfigurationHandler_1.ConfigurationHandler.isConfigurationAvailable()) {
27
- Logger.info('React Native plugin has not been started yet! Touch will not be reported!');
28
- if (origFunction != null) {
29
- return origFunction(event);
30
- }
31
- }
32
- else {
33
- let finalNameOfAction = nameOfAction;
34
- if (!(0, IDynatraceProperties_1.isDynatraceNaming)(props) &&
35
- ConfigurationHandler_1.ConfigurationHandler.isActionNamePrivacyEnabled()) {
36
- finalNameOfAction = (0, TypesUtil_1.getNameForType)(type);
37
- }
38
- const action = Dynatrace.enterAutoAction(`Touch on ${finalNameOfAction}`);
39
- if (origFunction != null) {
40
- let isSyncError = true;
41
- try {
42
- const returnValue = origFunction(event);
43
- if (_isPromise(returnValue)) {
44
- isSyncError = false;
45
- return Promise.resolve(returnValue).finally(() => {
46
- action.leaveAction();
47
- });
48
- }
49
- else {
50
- action.leaveAction();
51
- }
52
- isSyncError = false;
53
- }
54
- finally {
55
- if (isSyncError) {
56
- action.leaveAction();
57
- }
58
- }
59
- }
60
- else {
61
- action.leaveAction();
62
- }
63
- }
64
- };
65
- wrappedFunction._dtWrapped = true;
66
- return wrappedFunction;
67
- },
68
- _findActionName(props, children) {
69
- if ((0, IDynatraceProperties_1.isDynatraceNaming)(props)) {
70
- return props.dtActionName;
71
- }
72
- else if (this._isPropsButton(props) && props.title != null) {
73
- return props.title;
74
- }
75
- else if (props.accessibilityLabel != null) {
76
- return props.accessibilityLabel;
77
- }
78
- else if (children != null && children.length > 0) {
79
- if (children.length === 1 && typeof children[0] === 'string') {
80
- return children[0];
81
- }
82
- else {
83
- return this._walkChildrenToFindText(children);
84
- }
85
- }
86
- else if (props.children != null) {
87
- if (typeof props.children === 'string') {
88
- return props.children;
89
- }
90
- else {
91
- return this._walkChildrenToFindText(props.children);
92
- }
93
- }
94
- return null;
95
- },
96
- _isPropsButton: (props) => props.title != null,
97
- _isImageButton: (props) => props.source != null,
98
- _isLongPress: (props) => props.onLongPress != null,
99
- _isImageURISourceType: (source) => source.uri != null,
100
- _isIconButton: (element) => {
101
- const type = element.type;
102
- return type != null && type.name === 'Icon';
103
- },
104
- _walkTreeToFindText(element) {
105
- if (element == null || element.props == null) {
106
- return null;
107
- }
108
- else if (this._isImageButton(element.props)) {
109
- if (this._isImageURISourceType(element.props.source)) {
110
- return 'Image Button: ' + element.props.source.uri;
111
- }
112
- else {
113
- return 'Image Button';
114
- }
115
- }
116
- else if (this._isIconButton(element) && element.props.name != null) {
117
- return 'Icon Button: ' + element.props.name;
118
- }
119
- return this._walkChildrenToFindText(element.props.children);
120
- },
121
- _walkChildrenToFindText(children) {
122
- if (typeof children === 'string') {
123
- return children;
124
- }
125
- else if (Array.isArray(children)) {
126
- for (const child of children) {
127
- if (this._isReactElement(child)) {
128
- const name = this._walkTreeToFindText(child);
129
- if (name != null) {
130
- return name;
131
- }
132
- }
133
- else if (typeof child === 'string') {
134
- return child;
135
- }
136
- else if (Array.isArray(child)) {
137
- return this._walkChildrenToFindText(child);
138
- }
139
- }
140
- return null;
141
- }
142
- else {
143
- return this._walkTreeToFindText(children);
144
- }
145
- },
146
- _isReactElement: (node) => node != null && node.props != null,
147
- });
148
- exports.TouchableHelper = TouchableHelper;
149
- const _isPromise = (object) => object != null &&
150
- typeof object.then === 'function' &&
151
- typeof object.catch === 'function';