@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.
- package/README.md +33 -15
- package/android/build.gradle +1 -1
- package/files/plugin.gradle +1 -1
- package/instrumentation/jsx/CreateElement.js +1 -1
- package/instrumentation/jsx/ElementHelper.js +2 -16
- package/instrumentation/jsx/IDynatraceProperties.js +0 -7
- package/instrumentation/jsx/JsxDevRuntime.js +2 -7
- package/instrumentation/jsx/JsxRuntime.js +2 -7
- package/instrumentation/jsx/components/ComponentUtil.js +12 -1
- package/instrumentation/jsx/components/Picker.js +3 -3
- package/instrumentation/jsx/components/RefreshControl.js +3 -3
- package/instrumentation/jsx/components/Switch.js +4 -4
- package/instrumentation/libs/community/gesture-handler/Touchables.js +7 -37
- package/instrumentation/libs/react-native/Touchables.js +8 -47
- package/instrumentation/libs/withOnPressMonitoring.js +121 -0
- package/instrumentation/model/Types.js +3 -15
- package/instrumentation/model/TypesUtil.js +5 -34
- package/lib/next/Dynatrace.js +3 -0
- package/lib/next/events/spec/EventSpecContstants.js +1 -1
- package/package.json +6 -4
- package/react-native-dynatrace.podspec +1 -1
- package/scripts/Ios.js +1 -1
- package/scripts/Logger.js +20 -1
- package/scripts/core/InstrumentCall.js +62 -34
- package/scripts/core/LineOffsetAnalyzeCall.js +18 -15
- package/scripts/util/ReactOptions.js +21 -0
- package/scripts/util/SourceMapUtil.js +4 -4
- package/src/instrumentation/jsx/IDynatraceProperties.ts +15 -0
- package/typings/react-native-dynatrace.d.ts +9 -0
- package/instrumentation/jsx/components/Touchable.js +0 -151
package/lib/next/Dynatrace.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
'
|
|
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
|
-
|
|
16
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
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';
|