@dynatrace/react-native-plugin 2.331.1 → 2.333.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 +96 -6
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/dynatrace/android/agent/DynatraceRNBridgeImpl.kt +7 -1
- package/android/src/main/java/com/dynatrace/android/agent/DynatraceUtils.kt +1 -0
- package/android/src/new/java/com/dynatrace/android/agent/DynatraceRNBridge.kt +1 -0
- package/android/src/old/java/com/dynatrace/android/agent/DynatraceRNBridge.kt +2 -1
- package/files/plugin.gradle +1 -1
- package/instrumentation/DynatraceInstrumentation.js +1 -1
- package/instrumentation/jsx/CreateElement.js +106 -6
- package/instrumentation/jsx/JsxDevRuntime.js +2 -6
- package/instrumentation/jsx/JsxRuntime.js +2 -6
- package/instrumentation/libs/withOnPressMonitoring.js +49 -3
- package/ios/DynatraceRNBridge.mm +8 -1
- package/lib/core/Application.js +2 -0
- package/lib/core/Dynatrace.js +2 -1
- package/lib/core/UserPrivacyOptions.js +8 -1
- package/lib/core/configuration/ConfigurationHandler.js +21 -0
- package/lib/features/ui-interaction/Config.js +36 -0
- package/lib/features/ui-interaction/IUserInteractionEvent.js +16 -0
- package/lib/features/ui-interaction/Plugin.js +945 -0
- package/lib/features/ui-interaction/RootDetection.js +51 -0
- package/lib/features/ui-interaction/RootWrapper.js +236 -0
- package/lib/features/ui-interaction/Run.js +34 -0
- package/lib/features/ui-interaction/Runtime.js +1494 -0
- package/lib/features/ui-interaction/Types.js +75 -0
- package/lib/next/Dynatrace.js +1 -1
- package/lib/next/configuration/INativeRuntimeConfiguration.js +1 -0
- package/lib/next/configuration/RuntimeConfigurationObserver.js +47 -12
- package/lib/next/events/EventPipeline.js +9 -0
- package/package.json +19 -10
- package/react-native-dynatrace.podspec +1 -1
- package/scripts/Config.js +1 -0
- package/src/lib/core/interface/NativeDynatraceBridge.ts +1 -0
- package/types.d.ts +22 -9
|
@@ -0,0 +1,945 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyDTUserInteraction = void 0;
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const micromatch_1 = require("micromatch");
|
|
7
|
+
const jsc = require("jscodeshift");
|
|
8
|
+
const Types_1 = require("./Types");
|
|
9
|
+
const STOP_IDS_BASE = Object.values(Types_1.StopId).filter((x) => x !== Types_1.StopId.Segment && x !== Types_1.StopId.TabButton);
|
|
10
|
+
const STOP_MEMBER_BASE = Object.values(Types_1.StopMemberBase);
|
|
11
|
+
const TAB_BASE = Object.values(Types_1.TabBase);
|
|
12
|
+
const STOP_MEMBER_PROPS = Object.values(Types_1.StopMemberProp);
|
|
13
|
+
const DANGEROUS_WRAP_DENY = Object.values(Types_1.DangerousWrapDeny);
|
|
14
|
+
const PRESS_KEYS = Object.values(Types_1.PressKey);
|
|
15
|
+
const NODE_MODULES_UI_TAGS = [
|
|
16
|
+
'View',
|
|
17
|
+
'Text',
|
|
18
|
+
'Image',
|
|
19
|
+
'ScrollView',
|
|
20
|
+
'FlatList',
|
|
21
|
+
'SectionList',
|
|
22
|
+
'Pressable',
|
|
23
|
+
'TouchableOpacity',
|
|
24
|
+
'TouchableHighlight',
|
|
25
|
+
'TouchableWithoutFeedback',
|
|
26
|
+
'TouchableNativeFeedback',
|
|
27
|
+
'Modal',
|
|
28
|
+
'SafeAreaView',
|
|
29
|
+
'PlatformPressable',
|
|
30
|
+
];
|
|
31
|
+
const PATH_PREFIX_DEFAULT_NAME = 'PathPrefix';
|
|
32
|
+
const isPathInside = (baseDir, candidatePath) => {
|
|
33
|
+
const resolvedBase = path.resolve(baseDir);
|
|
34
|
+
const resolvedCandidate = path.resolve(candidatePath);
|
|
35
|
+
return (resolvedCandidate === resolvedBase ||
|
|
36
|
+
resolvedCandidate.startsWith(`${resolvedBase}${path.sep}`));
|
|
37
|
+
};
|
|
38
|
+
var GestureParentName;
|
|
39
|
+
(function (GestureParentName) {
|
|
40
|
+
GestureParentName["PanGestureHandler"] = "PanGestureHandler";
|
|
41
|
+
GestureParentName["TapGestureHandler"] = "TapGestureHandler";
|
|
42
|
+
GestureParentName["FlingGestureHandler"] = "FlingGestureHandler";
|
|
43
|
+
GestureParentName["NativeViewGestureHandler"] = "NativeViewGestureHandler";
|
|
44
|
+
GestureParentName["LongPressGestureHandler"] = "LongPressGestureHandler";
|
|
45
|
+
GestureParentName["RotationGestureHandler"] = "RotationGestureHandler";
|
|
46
|
+
GestureParentName["PinchGestureHandler"] = "PinchGestureHandler";
|
|
47
|
+
GestureParentName["ForceTouchGestureHandler"] = "ForceTouchGestureHandler";
|
|
48
|
+
GestureParentName["GestureDetector"] = "GestureDetector";
|
|
49
|
+
})(GestureParentName || (GestureParentName = {}));
|
|
50
|
+
var ChildWrapOnly;
|
|
51
|
+
(function (ChildWrapOnly) {
|
|
52
|
+
ChildWrapOnly["TouchableOpacity"] = "TouchableOpacity";
|
|
53
|
+
ChildWrapOnly["TouchableHighlight"] = "TouchableHighlight";
|
|
54
|
+
})(ChildWrapOnly || (ChildWrapOnly = {}));
|
|
55
|
+
const TAP_HANDLER_OVERLAY_TARGETS = [
|
|
56
|
+
{
|
|
57
|
+
pkg: 'react-native-gesture-handler',
|
|
58
|
+
fileSubstring: 'DrawerLayout',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
pkg: '@react-navigation/drawer',
|
|
62
|
+
fileSubstring: '@react-navigation/drawer',
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
const shouldInstrumentTapHandlerInFile = (state, pkg) => {
|
|
66
|
+
if (!pkg) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
const rel = path.relative(process.cwd(), state.file).replace(/\\/g, '/');
|
|
70
|
+
return TAP_HANDLER_OVERLAY_TARGETS.some((t) => t.pkg === pkg && rel.includes(t.fileSubstring));
|
|
71
|
+
};
|
|
72
|
+
const getName = (j, n) => {
|
|
73
|
+
if (j.JSXIdentifier.check(n)) {
|
|
74
|
+
return n.name;
|
|
75
|
+
}
|
|
76
|
+
if (j.JSXMemberExpression.check(n)) {
|
|
77
|
+
let c = n;
|
|
78
|
+
while (j.JSXMemberExpression.check(c)) {
|
|
79
|
+
c = c.property;
|
|
80
|
+
}
|
|
81
|
+
return j.JSXIdentifier.check(c) ? c.name : 'Anonymous';
|
|
82
|
+
}
|
|
83
|
+
return 'Anonymous';
|
|
84
|
+
};
|
|
85
|
+
const getBaseAndPropFromMemberExpression = (j, expr) => {
|
|
86
|
+
let obj = expr.object;
|
|
87
|
+
while (j.JSXMemberExpression.check(obj)) {
|
|
88
|
+
obj = obj.object;
|
|
89
|
+
}
|
|
90
|
+
const base = j.JSXIdentifier.check(obj) ? obj.name : null;
|
|
91
|
+
const prop = j.JSXIdentifier.check(expr.property)
|
|
92
|
+
? expr.property.name
|
|
93
|
+
: null;
|
|
94
|
+
return { base, prop };
|
|
95
|
+
};
|
|
96
|
+
const isStoppedMember = (j, el) => {
|
|
97
|
+
if (!j.JSXMemberExpression.check(el.name)) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
const { base, prop } = getBaseAndPropFromMemberExpression(j, el.name);
|
|
101
|
+
return !!(base &&
|
|
102
|
+
STOP_MEMBER_BASE.includes(base) &&
|
|
103
|
+
prop &&
|
|
104
|
+
STOP_MEMBER_PROPS.includes(prop));
|
|
105
|
+
};
|
|
106
|
+
const isTabScreen = (j, el) => {
|
|
107
|
+
if (!j.JSXMemberExpression.check(el.name)) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
const { base, prop } = getBaseAndPropFromMemberExpression(j, el.name);
|
|
111
|
+
return !!(base &&
|
|
112
|
+
TAB_BASE.includes(base) &&
|
|
113
|
+
prop === Types_1.StopMemberProp.Screen);
|
|
114
|
+
};
|
|
115
|
+
const isStoppedElementName = (name, segmentName, tabButtonName, rootName) => name === Types_1.Literals.ReactFragmentFQN ||
|
|
116
|
+
STOP_IDS_BASE.includes(name) ||
|
|
117
|
+
name === segmentName ||
|
|
118
|
+
name === tabButtonName ||
|
|
119
|
+
name === rootName;
|
|
120
|
+
const ensureNamedImport = (j, root, from, local) => {
|
|
121
|
+
const has = root
|
|
122
|
+
.find(j.ImportDeclaration, { source: { value: from } })
|
|
123
|
+
.filter((p) => (p.node.specifiers || []).some((s) => {
|
|
124
|
+
var _a;
|
|
125
|
+
return j.ImportSpecifier.check(s) &&
|
|
126
|
+
((_a = (j.Identifier.check(s.local)
|
|
127
|
+
? s.local.name
|
|
128
|
+
: undefined)) !== null && _a !== void 0 ? _a : (j.Identifier.check(s.imported)
|
|
129
|
+
? s.imported.name
|
|
130
|
+
: undefined)) === local;
|
|
131
|
+
}))
|
|
132
|
+
.size() > 0;
|
|
133
|
+
if (has) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const decl = j.importDeclaration([j.importSpecifier(j.identifier(local))], j.stringLiteral(from));
|
|
137
|
+
const first = root.find(j.ImportDeclaration).at(0);
|
|
138
|
+
if (first.size() > 0) {
|
|
139
|
+
first.insertBefore(decl);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
root.get().node.program.body.unshift(decl);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
const hasNamedImportBinding = (j, root, local) => root
|
|
146
|
+
.find(j.ImportDeclaration)
|
|
147
|
+
.filter((p) => (p.node.specifiers || []).some((s) => {
|
|
148
|
+
var _a;
|
|
149
|
+
return j.ImportSpecifier.check(s) &&
|
|
150
|
+
((_a = (j.Identifier.check(s.local) ? s.local.name : undefined)) !== null && _a !== void 0 ? _a : (j.Identifier.check(s.imported)
|
|
151
|
+
? s.imported.name
|
|
152
|
+
: undefined)) === local;
|
|
153
|
+
}))
|
|
154
|
+
.size() > 0;
|
|
155
|
+
const shouldSkipFile = (file, opts) => {
|
|
156
|
+
if (!(file !== null && file !== void 0 ? file : '')) {
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
const rel = path.relative(process.cwd(), file);
|
|
160
|
+
const relUnix = rel.replace(/\\/g, '/');
|
|
161
|
+
if (rel.includes(`dt${path.sep}runtime${path.sep}`)) {
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
const isNodeModulesFile = rel.includes(`node_modules${path.sep}`);
|
|
165
|
+
const isDrawerModule = rel.includes(`node_modules${path.sep}@react-navigation${path.sep}drawer`);
|
|
166
|
+
const includePatterns = opts === null || opts === void 0 ? void 0 : opts.include;
|
|
167
|
+
const excludePatterns = opts === null || opts === void 0 ? void 0 : opts.exclude;
|
|
168
|
+
if (!isNodeModulesFile && !isDrawerModule) {
|
|
169
|
+
return ((!!includePatterns && !(0, micromatch_1.isMatch)(relUnix, includePatterns)) ||
|
|
170
|
+
(!!excludePatterns && (0, micromatch_1.isMatch)(relUnix, excludePatterns)));
|
|
171
|
+
}
|
|
172
|
+
return false;
|
|
173
|
+
};
|
|
174
|
+
const valueToExpression = (j, value) => {
|
|
175
|
+
if (value === null) {
|
|
176
|
+
return j.nullLiteral();
|
|
177
|
+
}
|
|
178
|
+
const t = typeof value;
|
|
179
|
+
if (t === 'string') {
|
|
180
|
+
return j.stringLiteral(value);
|
|
181
|
+
}
|
|
182
|
+
if (t === 'number') {
|
|
183
|
+
return j.numericLiteral(value);
|
|
184
|
+
}
|
|
185
|
+
if (t === 'boolean') {
|
|
186
|
+
return j.booleanLiteral(value);
|
|
187
|
+
}
|
|
188
|
+
if (Array.isArray(value)) {
|
|
189
|
+
return j.arrayExpression(value.map((x) => valueToExpression(j, x)));
|
|
190
|
+
}
|
|
191
|
+
if (t === 'object') {
|
|
192
|
+
return j.objectExpression(Object.keys(value).map((k) => j.objectProperty(j.identifier(k), valueToExpression(j, value[k]))));
|
|
193
|
+
}
|
|
194
|
+
return j.nullLiteral();
|
|
195
|
+
};
|
|
196
|
+
const injectGlobalPrefixOnce = (j, root, pref) => {
|
|
197
|
+
if (!pref || !Array.isArray(pref) || pref.length === 0) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const prefixIdent = j.identifier('__AN_PREFIX');
|
|
201
|
+
const stmt = j.expressionStatement(j.logicalExpression('&&', j.binaryExpression('!==', j.unaryExpression('typeof', j.identifier('globalThis')), j.literal('undefined')), j.logicalExpression('||', j.memberExpression(j.identifier('globalThis'), prefixIdent), j.assignmentExpression('=', j.memberExpression(j.identifier('globalThis'), prefixIdent), valueToExpression(j, pref)))));
|
|
202
|
+
root.get().node.program.body.unshift(stmt);
|
|
203
|
+
};
|
|
204
|
+
const parentIsNavigator = (j, p) => {
|
|
205
|
+
const par = p.parentPath;
|
|
206
|
+
if (!par || !par.value || !j.JSXElement.check(par.value)) {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
const op = par.value.openingElement;
|
|
210
|
+
if (!j.JSXMemberExpression.check(op.name)) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
return (j.JSXIdentifier.check(op.name.property) &&
|
|
214
|
+
op.name.property.name === Types_1.Literals.NavigatorProp);
|
|
215
|
+
};
|
|
216
|
+
const makeUniqueSuffix = (s) => {
|
|
217
|
+
const ts = Date.now().toString(36);
|
|
218
|
+
const rand = Math.random().toString(36).slice(2, 8);
|
|
219
|
+
const seq = (s.seq++).toString(36);
|
|
220
|
+
return `${Types_1.Literals.KeySuffixPrefix}${ts}_${seq}_${rand}`;
|
|
221
|
+
};
|
|
222
|
+
const ensureOptionsObjectAttr = (j, openingEl) => {
|
|
223
|
+
let optionsAttr = (openingEl.attributes || []).find((a) => j.JSXAttribute.check(a) &&
|
|
224
|
+
j.JSXIdentifier.check(a.name) &&
|
|
225
|
+
a.name.name === Types_1.AttrName.Options);
|
|
226
|
+
if (!optionsAttr) {
|
|
227
|
+
optionsAttr = j.jsxAttribute(j.jsxIdentifier(Types_1.AttrName.Options), j.jsxExpressionContainer(j.objectExpression([])));
|
|
228
|
+
openingEl.attributes.push(optionsAttr);
|
|
229
|
+
}
|
|
230
|
+
return optionsAttr;
|
|
231
|
+
};
|
|
232
|
+
const injectTabBarButtonIntoOptions = (j, optionsAttr, withMask, tabButtonName) => {
|
|
233
|
+
if (!j.JSXAttribute.check(optionsAttr) ||
|
|
234
|
+
!j.JSXExpressionContainer.check(optionsAttr.value)) {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
const expr = optionsAttr.value.expression;
|
|
238
|
+
if (!j.ObjectExpression.check(expr)) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
const already = (expr.properties || []).some((p) => j.ObjectProperty.check(p) &&
|
|
242
|
+
((j.Identifier.check(p.key) &&
|
|
243
|
+
p.key.name === Types_1.AttrName.TabBarButton) ||
|
|
244
|
+
(j.StringLiteral.check(p.key) &&
|
|
245
|
+
p.key.value === Types_1.AttrName.TabBarButton)));
|
|
246
|
+
if (already) {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
const paramsId = j.identifier('props');
|
|
250
|
+
const attrs = [j.jsxSpreadAttribute(paramsId)];
|
|
251
|
+
if (withMask) {
|
|
252
|
+
attrs.push(j.jsxAttribute(j.jsxIdentifier(Types_1.AttrName.DtUIMask), j.jsxExpressionContainer(j.booleanLiteral(true))));
|
|
253
|
+
}
|
|
254
|
+
const opening = j.jsxOpeningElement(j.jsxIdentifier(tabButtonName), attrs, true);
|
|
255
|
+
const tabBtnEl = j.jsxElement(opening, null, []);
|
|
256
|
+
const arrow = j.arrowFunctionExpression([paramsId], tabBtnEl);
|
|
257
|
+
expr.properties.push(j.objectProperty(j.identifier(Types_1.AttrName.TabBarButton), arrow));
|
|
258
|
+
return true;
|
|
259
|
+
};
|
|
260
|
+
const pickAndRemoveDtUIMask = (j, openingEl) => {
|
|
261
|
+
const idx = (openingEl.attributes || []).findIndex((a) => j.JSXAttribute.check(a) &&
|
|
262
|
+
j.JSXIdentifier.check(a.name) &&
|
|
263
|
+
a.name.name === Types_1.AttrName.DtUIMask);
|
|
264
|
+
if (idx === -1) {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
const attr = openingEl.attributes[idx];
|
|
268
|
+
let val = true;
|
|
269
|
+
if (attr.value) {
|
|
270
|
+
if (j.JSXExpressionContainer.check(attr.value) &&
|
|
271
|
+
j.BooleanLiteral.check(attr.value.expression)) {
|
|
272
|
+
val = attr.value.expression.value;
|
|
273
|
+
}
|
|
274
|
+
else if (j.JSXExpressionContainer.check(attr.value) &&
|
|
275
|
+
j.Identifier.check(attr.value.expression)) {
|
|
276
|
+
val = true;
|
|
277
|
+
}
|
|
278
|
+
else if (j.StringLiteral.check(attr.value)) {
|
|
279
|
+
val = attr.value.value.toLowerCase() !== 'false';
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
openingEl.attributes.splice(idx, 1);
|
|
283
|
+
return !!val;
|
|
284
|
+
};
|
|
285
|
+
const cloneJsxElement = (j, el) => {
|
|
286
|
+
const open = j.jsxOpeningElement(el.openingElement.name, el.openingElement.attributes ? [...el.openingElement.attributes] : [], el.openingElement.selfClosing);
|
|
287
|
+
const close = el.closingElement
|
|
288
|
+
? j.jsxClosingElement(el.closingElement.name)
|
|
289
|
+
: null;
|
|
290
|
+
const children = (el.children || []).map((c) => {
|
|
291
|
+
if (j.JSXElement.check(c)) {
|
|
292
|
+
return cloneJsxElement(j, c);
|
|
293
|
+
}
|
|
294
|
+
if (j.JSXText.check(c)) {
|
|
295
|
+
return j.jsxText(c.value);
|
|
296
|
+
}
|
|
297
|
+
if (j.JSXExpressionContainer.check(c)) {
|
|
298
|
+
return j.jsxExpressionContainer(c.expression);
|
|
299
|
+
}
|
|
300
|
+
return c;
|
|
301
|
+
});
|
|
302
|
+
return j.jsxElement(open, close, children);
|
|
303
|
+
};
|
|
304
|
+
const getNodeModulesPackageName = (filename) => {
|
|
305
|
+
if (!filename) {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
const rel = path.relative(process.cwd(), filename);
|
|
309
|
+
const parts = rel.split(path.sep);
|
|
310
|
+
const nmIndex = parts.indexOf('node_modules');
|
|
311
|
+
if (nmIndex < 0 || nmIndex >= parts.length - 1) {
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
const first = parts[nmIndex + 1];
|
|
315
|
+
if (!first) {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
if (first.startsWith('@')) {
|
|
319
|
+
if (nmIndex + 2 >= parts.length) {
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
const second = parts[nmIndex + 2];
|
|
323
|
+
return `${first}/${second}`;
|
|
324
|
+
}
|
|
325
|
+
return first;
|
|
326
|
+
};
|
|
327
|
+
const collectGestureHandlerInfo = (j, root) => {
|
|
328
|
+
const gestureHandlerElements = new Set();
|
|
329
|
+
const directGestureChildren = new Set();
|
|
330
|
+
root.find(j.JSXElement).forEach((p) => {
|
|
331
|
+
const node = p.node;
|
|
332
|
+
const el = node.openingElement;
|
|
333
|
+
const name = getName(j, el.name);
|
|
334
|
+
if (Object.values(GestureParentName).includes(name)) {
|
|
335
|
+
gestureHandlerElements.add(node);
|
|
336
|
+
const children = node.children || [];
|
|
337
|
+
for (const ch of children) {
|
|
338
|
+
if (j.JSXElement.check(ch)) {
|
|
339
|
+
directGestureChildren.add(ch);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
return { gestureHandlerElements, directGestureChildren };
|
|
345
|
+
};
|
|
346
|
+
const shouldWrapNodeModulesElement = (name, segmentName, tabButtonName, rootName, nodeModulesUITags) => {
|
|
347
|
+
if (!nodeModulesUITags.has(name)) {
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
if (isStoppedElementName(name, segmentName, tabButtonName, rootName)) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
if (DANGEROUS_WRAP_DENY.includes(name)) {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
return true;
|
|
357
|
+
};
|
|
358
|
+
const handleTabScreen = (ctx, _p, meta) => {
|
|
359
|
+
const { j, state } = ctx;
|
|
360
|
+
const { el, tabBtnName } = meta;
|
|
361
|
+
if (!isTabScreen(j, el)) {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
const withMask = !!pickAndRemoveDtUIMask(j, el);
|
|
365
|
+
const optionsAttr = ensureOptionsObjectAttr(j, el);
|
|
366
|
+
const changed = injectTabBarButtonIntoOptions(j, optionsAttr, withMask, tabBtnName);
|
|
367
|
+
if (changed) {
|
|
368
|
+
state.instrumented = true;
|
|
369
|
+
}
|
|
370
|
+
return true;
|
|
371
|
+
};
|
|
372
|
+
const handleNodeModulesUI = (ctx, p, meta) => {
|
|
373
|
+
const { j, state, isNodeModulesFile, nodeModulesPackageName, skipTags, skipChildrenOf, allowMemberExpr, nodeModulesUITags, gestureHandlerElements, directGestureChildren, } = ctx;
|
|
374
|
+
const { node, el, name, segName, tabBtnName, rootName } = meta;
|
|
375
|
+
if (!isNodeModulesFile) {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
const isGestureSelf = gestureHandlerElements.has(node);
|
|
379
|
+
const isDirectGestureChild = directGestureChildren.has(node);
|
|
380
|
+
const hasJsxChildren = (node.children || []).some((ch) => j.JSXElement.check(ch));
|
|
381
|
+
if (isGestureSelf) {
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
if (isDirectGestureChild && hasJsxChildren) {
|
|
385
|
+
const origChildren = node.children || [];
|
|
386
|
+
if (!origChildren.length) {
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
const newChildren = [];
|
|
390
|
+
for (const ch of origChildren) {
|
|
391
|
+
if (!j.JSXElement.check(ch)) {
|
|
392
|
+
newChildren.push(ch);
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
const childOpening = ch.openingElement;
|
|
396
|
+
const childName = getName(j, childOpening.name);
|
|
397
|
+
if (!shouldWrapNodeModulesElement(childName, segName, tabBtnName, rootName, nodeModulesUITags)) {
|
|
398
|
+
newChildren.push(ch);
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
const innerClone = cloneJsxElement(j, ch);
|
|
402
|
+
const segAttrs = [
|
|
403
|
+
j.jsxAttribute(j.jsxIdentifier(Types_1.AttrName.Name), j.stringLiteral(childName)),
|
|
404
|
+
j.jsxAttribute(j.jsxIdentifier(Types_1.AttrName.Key), j.stringLiteral(makeUniqueSuffix(state))),
|
|
405
|
+
];
|
|
406
|
+
const segOpen = j.jsxOpeningElement(j.jsxIdentifier(segName), segAttrs, false);
|
|
407
|
+
const segClose = j.jsxClosingElement(j.jsxIdentifier(segName));
|
|
408
|
+
const segElement = j.jsxElement(segOpen, segClose, [innerClone]);
|
|
409
|
+
let wrapped = segElement;
|
|
410
|
+
if (nodeModulesPackageName) {
|
|
411
|
+
const pathPrefixOpen = j.jsxOpeningElement(j.jsxIdentifier(PATH_PREFIX_DEFAULT_NAME), [
|
|
412
|
+
j.jsxAttribute(j.jsxIdentifier('names'), j.jsxExpressionContainer(j.arrayExpression([
|
|
413
|
+
j.objectExpression([
|
|
414
|
+
j.objectProperty(j.identifier('name'), j.literal(nodeModulesPackageName)),
|
|
415
|
+
]),
|
|
416
|
+
]))),
|
|
417
|
+
], false);
|
|
418
|
+
const pathPrefixClose = j.jsxClosingElement(j.jsxIdentifier(PATH_PREFIX_DEFAULT_NAME));
|
|
419
|
+
wrapped = j.jsxElement(pathPrefixOpen, pathPrefixClose, [
|
|
420
|
+
segElement,
|
|
421
|
+
]);
|
|
422
|
+
}
|
|
423
|
+
newChildren.push(wrapped);
|
|
424
|
+
state.instrumented = true;
|
|
425
|
+
}
|
|
426
|
+
node.children = newChildren;
|
|
427
|
+
return state.instrumented;
|
|
428
|
+
}
|
|
429
|
+
if (skipTags.has(name)) {
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
const parent = p.parentPath;
|
|
433
|
+
if (parent && parent.value && j.JSXElement.check(parent.value)) {
|
|
434
|
+
const parentName = getName(j, parent.value.openingElement.name);
|
|
435
|
+
if (skipChildrenOf.has(parentName)) {
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (j.JSXMemberExpression.check(el.name)) {
|
|
440
|
+
const { base, prop } = getBaseAndPropFromMemberExpression(j, el.name);
|
|
441
|
+
const full = base && prop ? `${base}.${prop}` : null;
|
|
442
|
+
if (!full || !allowMemberExpr.has(full)) {
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const canWrapThisElement = shouldWrapNodeModulesElement(name, segName, tabBtnName, rootName, nodeModulesUITags);
|
|
447
|
+
if (!canWrapThisElement) {
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
const childEl = cloneJsxElement(j, node);
|
|
451
|
+
const segAttrs = [
|
|
452
|
+
j.jsxAttribute(j.jsxIdentifier(Types_1.AttrName.Name), j.stringLiteral(name)),
|
|
453
|
+
j.jsxAttribute(j.jsxIdentifier(Types_1.AttrName.Key), j.stringLiteral(makeUniqueSuffix(state))),
|
|
454
|
+
];
|
|
455
|
+
const segOpen = j.jsxOpeningElement(j.jsxIdentifier(segName), segAttrs, false);
|
|
456
|
+
const segClose = j.jsxClosingElement(j.jsxIdentifier(segName));
|
|
457
|
+
const segElement = j.jsxElement(segOpen, segClose, [childEl]);
|
|
458
|
+
if (!nodeModulesPackageName) {
|
|
459
|
+
p.replace(segElement);
|
|
460
|
+
state.instrumented = true;
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
const pathPrefixOpen = j.jsxOpeningElement(j.jsxIdentifier(PATH_PREFIX_DEFAULT_NAME), [
|
|
464
|
+
j.jsxAttribute(j.jsxIdentifier('names'), j.jsxExpressionContainer(j.arrayExpression([
|
|
465
|
+
j.objectExpression([
|
|
466
|
+
j.objectProperty(j.identifier('name'), j.literal(nodeModulesPackageName)),
|
|
467
|
+
]),
|
|
468
|
+
]))),
|
|
469
|
+
], false);
|
|
470
|
+
const pathPrefixClose = j.jsxClosingElement(j.jsxIdentifier(PATH_PREFIX_DEFAULT_NAME));
|
|
471
|
+
const wrapped = j.jsxElement(pathPrefixOpen, pathPrefixClose, [segElement]);
|
|
472
|
+
p.replace(wrapped);
|
|
473
|
+
state.instrumented = true;
|
|
474
|
+
return true;
|
|
475
|
+
};
|
|
476
|
+
const handleNavigatorParent = (ctx, p, _meta) => {
|
|
477
|
+
const { j } = ctx;
|
|
478
|
+
return parentIsNavigator(j, p);
|
|
479
|
+
};
|
|
480
|
+
const handleStoppedMemberHandler = (ctx, _p, meta) => {
|
|
481
|
+
const { j } = ctx;
|
|
482
|
+
const { el } = meta;
|
|
483
|
+
return isStoppedMember(j, el);
|
|
484
|
+
};
|
|
485
|
+
const handleStoppedElementHandler = (_ctx, _p, meta) => {
|
|
486
|
+
const { segName, tabBtnName, rootName, name } = meta;
|
|
487
|
+
return isStoppedElementName(name, segName, tabBtnName, rootName);
|
|
488
|
+
};
|
|
489
|
+
const handleDangerousWrapHandler = (ctx, _p, meta) => {
|
|
490
|
+
const { j, state } = ctx;
|
|
491
|
+
const { el, name } = meta;
|
|
492
|
+
if (!DANGEROUS_WRAP_DENY.includes(name)) {
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
const attrs = el.attributes || [];
|
|
496
|
+
let touched = false;
|
|
497
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
498
|
+
const a = attrs[i];
|
|
499
|
+
if (!j.JSXAttribute.check(a)) {
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
const propName = j.JSXIdentifier.check(a.name) ? a.name.name : null;
|
|
503
|
+
if (propName === null || !PRESS_KEYS.includes(propName)) {
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
if (!a.value) {
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
if (j.JSXExpressionContainer.check(a.value)) {
|
|
510
|
+
const expr = a.value.expression;
|
|
511
|
+
const orig = j.JSXEmptyExpression.check(expr)
|
|
512
|
+
? j.identifier('undefined')
|
|
513
|
+
: expr;
|
|
514
|
+
const phase = j.literal(propName);
|
|
515
|
+
const body = j.blockStatement([
|
|
516
|
+
j.variableDeclaration('const', [
|
|
517
|
+
j.variableDeclarator(j.identifier(Types_1.IdName.DtEmit), j.memberExpression(j.identifier('console'), j.identifier('log'))),
|
|
518
|
+
]),
|
|
519
|
+
j.expressionStatement(j.callExpression(j.identifier(Types_1.IdName.DtEmit), [
|
|
520
|
+
j.literal(Types_1.Literals.RawHandlerTag),
|
|
521
|
+
phase,
|
|
522
|
+
])),
|
|
523
|
+
j.returnStatement(j.conditionalExpression(j.binaryExpression('===', j.unaryExpression('typeof', orig), j.literal('function')), j.callExpression(orig, [
|
|
524
|
+
j.spreadElement(j.identifier('args')),
|
|
525
|
+
]), j.identifier('undefined'))),
|
|
526
|
+
]);
|
|
527
|
+
a.value = j.jsxExpressionContainer(j.arrowFunctionExpression([j.restElement(j.identifier('args'))], body));
|
|
528
|
+
touched = true;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if (touched) {
|
|
532
|
+
state.instrumented = true;
|
|
533
|
+
}
|
|
534
|
+
return true;
|
|
535
|
+
};
|
|
536
|
+
const handleDefaultSegmentWrap = (ctx, p, meta) => {
|
|
537
|
+
const { j, state, isDrawerModuleFile, isNodeModulesFile, skipTags, skipChildrenOf, gestureHandlerElements, directGestureChildren, } = ctx;
|
|
538
|
+
const { node, el, name, segName, tabBtnName, rootName } = meta;
|
|
539
|
+
const isGestureSelf = gestureHandlerElements.has(node);
|
|
540
|
+
const isDirectGestureChild = directGestureChildren.has(node);
|
|
541
|
+
if (isGestureSelf || isDirectGestureChild) {
|
|
542
|
+
return true;
|
|
543
|
+
}
|
|
544
|
+
if (isNodeModulesFile) {
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
547
|
+
if (skipTags.has(name)) {
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
const parent = p.parentPath;
|
|
551
|
+
if (parent && parent.value && j.JSXElement.check(parent.value)) {
|
|
552
|
+
const parentName = getName(j, parent.value.openingElement.name);
|
|
553
|
+
if (skipChildrenOf.has(parentName)) {
|
|
554
|
+
return false;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
const segAttrs = [
|
|
558
|
+
j.jsxAttribute(j.jsxIdentifier(Types_1.AttrName.Name), j.stringLiteral(name)),
|
|
559
|
+
];
|
|
560
|
+
const origAttrs = el.attributes || [];
|
|
561
|
+
const keyAttr = origAttrs.find((a) => j.JSXAttribute.check(a) &&
|
|
562
|
+
j.JSXIdentifier.check(a.name) &&
|
|
563
|
+
a.name.name === Types_1.AttrName.Key);
|
|
564
|
+
if (keyAttr) {
|
|
565
|
+
segAttrs.push(keyAttr);
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
segAttrs.push(j.jsxAttribute(j.jsxIdentifier(Types_1.AttrName.Key), j.stringLiteral(makeUniqueSuffix(state))));
|
|
569
|
+
}
|
|
570
|
+
const childEl = cloneJsxElement(j, node);
|
|
571
|
+
if (name === 'TouchableOpacity' || name === 'TouchableHighlight') {
|
|
572
|
+
const tpChildren = childEl.children || [];
|
|
573
|
+
const newTpChildren = [];
|
|
574
|
+
for (const ch of tpChildren) {
|
|
575
|
+
if (!j.JSXElement.check(ch)) {
|
|
576
|
+
newTpChildren.push(ch);
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
const grandOpening = ch.openingElement;
|
|
580
|
+
const grandName = getName(j, grandOpening.name);
|
|
581
|
+
if (isStoppedElementName(grandName, segName, tabBtnName, rootName) ||
|
|
582
|
+
DANGEROUS_WRAP_DENY.includes(grandName)) {
|
|
583
|
+
newTpChildren.push(ch);
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
const grandSegAttrs = [
|
|
587
|
+
j.jsxAttribute(j.jsxIdentifier(Types_1.AttrName.Name), j.stringLiteral(grandName)),
|
|
588
|
+
j.jsxAttribute(j.jsxIdentifier(Types_1.AttrName.Key), j.stringLiteral(makeUniqueSuffix(state))),
|
|
589
|
+
];
|
|
590
|
+
const grandSegOpen = j.jsxOpeningElement(j.jsxIdentifier(segName), grandSegAttrs, false);
|
|
591
|
+
const grandSegClose = j.jsxClosingElement(j.jsxIdentifier(segName));
|
|
592
|
+
const grandInner = cloneJsxElement(j, ch);
|
|
593
|
+
const wrappedGrand = j.jsxElement(grandSegOpen, grandSegClose, [
|
|
594
|
+
grandInner,
|
|
595
|
+
]);
|
|
596
|
+
newTpChildren.push(wrappedGrand);
|
|
597
|
+
state.instrumented = true;
|
|
598
|
+
}
|
|
599
|
+
childEl.children = newTpChildren;
|
|
600
|
+
}
|
|
601
|
+
if (!isDrawerModuleFile && name === 'View') {
|
|
602
|
+
const newChildren = [];
|
|
603
|
+
for (const ch of childEl.children || []) {
|
|
604
|
+
if (!j.JSXElement.check(ch)) {
|
|
605
|
+
newChildren.push(ch);
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
const childOpening = ch.openingElement;
|
|
609
|
+
const childName = getName(j, childOpening.name);
|
|
610
|
+
if (childName in ChildWrapOnly) {
|
|
611
|
+
const tpClone = cloneJsxElement(j, ch);
|
|
612
|
+
const tpChildren = tpClone.children || [];
|
|
613
|
+
const newTpChildren = [];
|
|
614
|
+
for (const grand of tpChildren) {
|
|
615
|
+
if (!j.JSXElement.check(grand)) {
|
|
616
|
+
newTpChildren.push(grand);
|
|
617
|
+
continue;
|
|
618
|
+
}
|
|
619
|
+
const grandOpening = grand.openingElement;
|
|
620
|
+
const grandName = getName(j, grandOpening.name);
|
|
621
|
+
if (isStoppedElementName(grandName, segName, tabBtnName, rootName) ||
|
|
622
|
+
DANGEROUS_WRAP_DENY.includes(grandName)) {
|
|
623
|
+
newTpChildren.push(grand);
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
const grandSegAttrs = [
|
|
627
|
+
j.jsxAttribute(j.jsxIdentifier(Types_1.AttrName.Name), j.stringLiteral(grandName)),
|
|
628
|
+
j.jsxAttribute(j.jsxIdentifier(Types_1.AttrName.Key), j.stringLiteral(makeUniqueSuffix(state))),
|
|
629
|
+
];
|
|
630
|
+
const grandSegOpen = j.jsxOpeningElement(j.jsxIdentifier(segName), grandSegAttrs, false);
|
|
631
|
+
const grandSegClose = j.jsxClosingElement(j.jsxIdentifier(segName));
|
|
632
|
+
const grandInner = cloneJsxElement(j, grand);
|
|
633
|
+
const wrappedGrand = j.jsxElement(grandSegOpen, grandSegClose, [grandInner]);
|
|
634
|
+
newTpChildren.push(wrappedGrand);
|
|
635
|
+
state.instrumented = true;
|
|
636
|
+
}
|
|
637
|
+
tpClone.children = newTpChildren;
|
|
638
|
+
newChildren.push(tpClone);
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
if (isStoppedElementName(childName, segName, tabBtnName, rootName)) {
|
|
642
|
+
newChildren.push(ch);
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
if (DANGEROUS_WRAP_DENY.includes(childName)) {
|
|
646
|
+
newChildren.push(ch);
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
const childSegAttrs = [
|
|
650
|
+
j.jsxAttribute(j.jsxIdentifier(Types_1.AttrName.Name), j.stringLiteral(childName)),
|
|
651
|
+
j.jsxAttribute(j.jsxIdentifier(Types_1.AttrName.Key), j.stringLiteral(makeUniqueSuffix(state))),
|
|
652
|
+
];
|
|
653
|
+
const childSegOpen = j.jsxOpeningElement(j.jsxIdentifier(segName), childSegAttrs, false);
|
|
654
|
+
const childSegClose = j.jsxClosingElement(j.jsxIdentifier(segName));
|
|
655
|
+
const innerClone = cloneJsxElement(j, ch);
|
|
656
|
+
const wrappedChild = j.jsxElement(childSegOpen, childSegClose, [
|
|
657
|
+
innerClone,
|
|
658
|
+
]);
|
|
659
|
+
newChildren.push(wrappedChild);
|
|
660
|
+
state.instrumented = true;
|
|
661
|
+
}
|
|
662
|
+
childEl.children = newChildren;
|
|
663
|
+
}
|
|
664
|
+
const segOpen = j.jsxOpeningElement(j.jsxIdentifier(segName), segAttrs, false);
|
|
665
|
+
const segClose = j.jsxClosingElement(j.jsxIdentifier(segName));
|
|
666
|
+
const wrapped = j.jsxElement(segOpen, segClose, [childEl]);
|
|
667
|
+
p.replace(wrapped);
|
|
668
|
+
state.instrumented = true;
|
|
669
|
+
return true;
|
|
670
|
+
};
|
|
671
|
+
const handleTapHandlerOverlay = (ctx, p, meta) => {
|
|
672
|
+
const { j, state, isNodeModulesFile, nodeModulesPackageName } = ctx;
|
|
673
|
+
const { node, name, segName, tabBtnName, rootName } = meta;
|
|
674
|
+
if (!isNodeModulesFile) {
|
|
675
|
+
return false;
|
|
676
|
+
}
|
|
677
|
+
if (name !== 'TapGestureHandler') {
|
|
678
|
+
return false;
|
|
679
|
+
}
|
|
680
|
+
if (!shouldInstrumentTapHandlerInFile(state, nodeModulesPackageName)) {
|
|
681
|
+
return false;
|
|
682
|
+
}
|
|
683
|
+
const children = node.children || [];
|
|
684
|
+
let targetIndex = -1;
|
|
685
|
+
for (let i = 0; i < children.length; i++) {
|
|
686
|
+
const ch = children[i];
|
|
687
|
+
if (j.JSXElement.check(ch)) {
|
|
688
|
+
targetIndex = i;
|
|
689
|
+
break;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
if (targetIndex === -1) {
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
const childEl = children[targetIndex];
|
|
696
|
+
if (!j.JSXElement.check(childEl)) {
|
|
697
|
+
return false;
|
|
698
|
+
}
|
|
699
|
+
const childName = getName(j, childEl.openingElement.name);
|
|
700
|
+
if (isStoppedElementName(childName, segName, tabBtnName, rootName) ||
|
|
701
|
+
DANGEROUS_WRAP_DENY.includes(childName)) {
|
|
702
|
+
return false;
|
|
703
|
+
}
|
|
704
|
+
const opening = childEl.openingElement;
|
|
705
|
+
const attrs = opening.attributes || [];
|
|
706
|
+
let styleAttrIndex = -1;
|
|
707
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
708
|
+
const a = attrs[i];
|
|
709
|
+
if (j.JSXAttribute.check(a) &&
|
|
710
|
+
j.JSXIdentifier.check(a.name) &&
|
|
711
|
+
a.name.name === 'style') {
|
|
712
|
+
styleAttrIndex = i;
|
|
713
|
+
break;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
const extraStyleObj = j.objectExpression([
|
|
717
|
+
j.objectProperty(j.identifier('borderWidth'), j.literal(2)),
|
|
718
|
+
j.objectProperty(j.identifier('borderColor'), j.literal('red')),
|
|
719
|
+
]);
|
|
720
|
+
if (styleAttrIndex === -1) {
|
|
721
|
+
const styleExpr = j.arrayExpression([extraStyleObj]);
|
|
722
|
+
attrs.push(j.jsxAttribute(j.jsxIdentifier('style'), j.jsxExpressionContainer(styleExpr)));
|
|
723
|
+
}
|
|
724
|
+
else {
|
|
725
|
+
const styleAttr = attrs[styleAttrIndex];
|
|
726
|
+
if (j.JSXExpressionContainer.check(styleAttr.value)) {
|
|
727
|
+
const expr = styleAttr.value.expression;
|
|
728
|
+
if (j.ArrayExpression.check(expr)) {
|
|
729
|
+
expr.elements.push(extraStyleObj);
|
|
730
|
+
}
|
|
731
|
+
else {
|
|
732
|
+
styleAttr.value = j.jsxExpressionContainer(j.arrayExpression([expr, extraStyleObj]));
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
else {
|
|
736
|
+
styleAttr.value = j.jsxExpressionContainer(j.arrayExpression([extraStyleObj]));
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
opening.attributes = attrs;
|
|
740
|
+
state.instrumented = true;
|
|
741
|
+
return true;
|
|
742
|
+
};
|
|
743
|
+
const handleDrawerOverlayPressable = (ctx, p, meta) => {
|
|
744
|
+
const { j, state, isDrawerModuleFile, isNodeModulesFile } = ctx;
|
|
745
|
+
const { node, segName } = meta;
|
|
746
|
+
if (!isNodeModulesFile || !isDrawerModuleFile) {
|
|
747
|
+
return false;
|
|
748
|
+
}
|
|
749
|
+
const children = node.children || [];
|
|
750
|
+
if (!children.length) {
|
|
751
|
+
return false;
|
|
752
|
+
}
|
|
753
|
+
let modified = false;
|
|
754
|
+
const newChildren = [];
|
|
755
|
+
for (let i = 0; i < children.length; i++) {
|
|
756
|
+
const ch = children[i];
|
|
757
|
+
if (!j.JSXElement.check(ch)) {
|
|
758
|
+
newChildren.push(ch);
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
const childOpening = ch.openingElement;
|
|
762
|
+
const childName = getName(j, childOpening.name);
|
|
763
|
+
if (childName !== 'Pressable') {
|
|
764
|
+
newChildren.push(ch);
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
const attrs = childOpening.attributes || [];
|
|
768
|
+
let isOverlayPressable = false;
|
|
769
|
+
for (let k = 0; k < attrs.length; k++) {
|
|
770
|
+
const a = attrs[k];
|
|
771
|
+
if (j.JSXAttribute.check(a) &&
|
|
772
|
+
j.JSXIdentifier.check(a.name) &&
|
|
773
|
+
a.name.name === 'style' &&
|
|
774
|
+
j.JSXExpressionContainer.check(a.value)) {
|
|
775
|
+
const expr = a.value.expression;
|
|
776
|
+
if (j.ArrayExpression.check(expr)) {
|
|
777
|
+
for (const el of expr.elements || []) {
|
|
778
|
+
if (el &&
|
|
779
|
+
j.MemberExpression.check(el) &&
|
|
780
|
+
j.Identifier.check(el.object) &&
|
|
781
|
+
el.object.name === 'styles' &&
|
|
782
|
+
j.Identifier.check(el.property) &&
|
|
783
|
+
el.property.name === 'pressable') {
|
|
784
|
+
isOverlayPressable = true;
|
|
785
|
+
break;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
else if (j.MemberExpression.check(expr) &&
|
|
790
|
+
j.Identifier.check(expr.object) &&
|
|
791
|
+
expr.object.name === 'styles' &&
|
|
792
|
+
j.Identifier.check(expr.property) &&
|
|
793
|
+
expr.property.name === 'pressable') {
|
|
794
|
+
isOverlayPressable = true;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
if (isOverlayPressable) {
|
|
798
|
+
break;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
if (!isOverlayPressable) {
|
|
802
|
+
newChildren.push(ch);
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
const segAttrs = [
|
|
806
|
+
j.jsxAttribute(j.jsxIdentifier(Types_1.AttrName.Name), j.stringLiteral('DrawerOverlayPressable')),
|
|
807
|
+
j.jsxAttribute(j.jsxIdentifier(Types_1.AttrName.Key), j.stringLiteral(makeUniqueSuffix(state))),
|
|
808
|
+
];
|
|
809
|
+
const segOpen = j.jsxOpeningElement(j.jsxIdentifier(segName), segAttrs, false);
|
|
810
|
+
const segClose = j.jsxClosingElement(j.jsxIdentifier(segName));
|
|
811
|
+
const pressClone = cloneJsxElement(j, ch);
|
|
812
|
+
const segElement = j.jsxElement(segOpen, segClose, [pressClone]);
|
|
813
|
+
newChildren.push(segElement);
|
|
814
|
+
modified = true;
|
|
815
|
+
}
|
|
816
|
+
if (!modified) {
|
|
817
|
+
return false;
|
|
818
|
+
}
|
|
819
|
+
node.children = newChildren;
|
|
820
|
+
state.instrumented = true;
|
|
821
|
+
return true;
|
|
822
|
+
};
|
|
823
|
+
const applyDTUserInteraction = (root, filename, options = {}) => {
|
|
824
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
825
|
+
const j = jsc.withParser('tsx');
|
|
826
|
+
const { gestureHandlerElements, directGestureChildren } = collectGestureHandlerInfo(j, root);
|
|
827
|
+
const isDrawerModuleFile = typeof filename === 'string' &&
|
|
828
|
+
filename.includes(`node_modules${path.sep}@react-navigation${path.sep}drawer`);
|
|
829
|
+
const isNodeModulesFile = typeof filename === 'string' &&
|
|
830
|
+
filename.includes(`node_modules${path.sep}`);
|
|
831
|
+
const nodeModulesPackageName = getNodeModulesPackageName(filename);
|
|
832
|
+
const state = {
|
|
833
|
+
file: filename,
|
|
834
|
+
skip: shouldSkipFile(filename, options),
|
|
835
|
+
seq: 0,
|
|
836
|
+
instrumented: false,
|
|
837
|
+
};
|
|
838
|
+
if (!state.skip &&
|
|
839
|
+
Array.isArray(options === null || options === void 0 ? void 0 : options.globalPrefix) &&
|
|
840
|
+
options.globalPrefix.length > 0) {
|
|
841
|
+
injectGlobalPrefixOnce(j, root, options.globalPrefix);
|
|
842
|
+
}
|
|
843
|
+
const segName = (_b = (_a = options === null || options === void 0 ? void 0 : options.componentNames) === null || _a === void 0 ? void 0 : _a.segment) !== null && _b !== void 0 ? _b : Types_1.Literals.DefaultSegmentName;
|
|
844
|
+
const tabBtnName = (_d = (_c = options === null || options === void 0 ? void 0 : options.componentNames) === null || _c === void 0 ? void 0 : _c.tabButton) !== null && _d !== void 0 ? _d : Types_1.Literals.DefaultTabButtonName;
|
|
845
|
+
const rootName = (_f = (_e = options === null || options === void 0 ? void 0 : options.componentNames) === null || _e === void 0 ? void 0 : _e.root) !== null && _f !== void 0 ? _f : Types_1.Literals.DefaultRootName;
|
|
846
|
+
const uiOptions = ((options === null || options === void 0 ? void 0 : options.ui) || {});
|
|
847
|
+
const skipTags = new Set(uiOptions.skipTags || []);
|
|
848
|
+
const skipChildrenOf = new Set(uiOptions.skipChildrenOf || []);
|
|
849
|
+
const defaultAllowMemberExpr = [
|
|
850
|
+
'Animated.View',
|
|
851
|
+
'Animated.Text',
|
|
852
|
+
'Reanimated.View',
|
|
853
|
+
'Reanimated.Text',
|
|
854
|
+
];
|
|
855
|
+
const allowMemberExpr = new Set([
|
|
856
|
+
...defaultAllowMemberExpr,
|
|
857
|
+
...(uiOptions.allowMemberExpressions || []),
|
|
858
|
+
]);
|
|
859
|
+
const nodeModulesUITags = new Set([
|
|
860
|
+
...NODE_MODULES_UI_TAGS,
|
|
861
|
+
...(uiOptions.extraNodeModulesTags || []),
|
|
862
|
+
]);
|
|
863
|
+
const ctx = {
|
|
864
|
+
j,
|
|
865
|
+
state,
|
|
866
|
+
options,
|
|
867
|
+
isDrawerModuleFile,
|
|
868
|
+
isNodeModulesFile,
|
|
869
|
+
nodeModulesPackageName,
|
|
870
|
+
skipTags,
|
|
871
|
+
skipChildrenOf,
|
|
872
|
+
allowMemberExpr,
|
|
873
|
+
nodeModulesUITags,
|
|
874
|
+
gestureHandlerElements,
|
|
875
|
+
directGestureChildren,
|
|
876
|
+
};
|
|
877
|
+
const handlers = [
|
|
878
|
+
handleTabScreen,
|
|
879
|
+
handleNavigatorParent,
|
|
880
|
+
handleStoppedMemberHandler,
|
|
881
|
+
handleStoppedElementHandler,
|
|
882
|
+
handleDangerousWrapHandler,
|
|
883
|
+
handleTapHandlerOverlay,
|
|
884
|
+
handleDrawerOverlayPressable,
|
|
885
|
+
handleNodeModulesUI,
|
|
886
|
+
handleDefaultSegmentWrap,
|
|
887
|
+
];
|
|
888
|
+
root.find(j.JSXElement).forEach((p) => {
|
|
889
|
+
if (state.skip) {
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
const node = p.node;
|
|
893
|
+
const el = node.openingElement;
|
|
894
|
+
const name = getName(j, el.name);
|
|
895
|
+
const meta = {
|
|
896
|
+
node,
|
|
897
|
+
el,
|
|
898
|
+
name,
|
|
899
|
+
segName,
|
|
900
|
+
tabBtnName,
|
|
901
|
+
rootName,
|
|
902
|
+
};
|
|
903
|
+
for (const h of handlers) {
|
|
904
|
+
if (h(ctx, p, meta)) {
|
|
905
|
+
break;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
});
|
|
909
|
+
if (!state.skip && state.instrumented) {
|
|
910
|
+
const importPath = options.runtimeImport || Types_1.Literals.DefaultRuntimeImport;
|
|
911
|
+
const segNameImport = (_h = (_g = options === null || options === void 0 ? void 0 : options.componentNames) === null || _g === void 0 ? void 0 : _g.segment) !== null && _h !== void 0 ? _h : Types_1.Literals.DefaultSegmentName;
|
|
912
|
+
const tabBtnNameImport = (_k = (_j = options === null || options === void 0 ? void 0 : options.componentNames) === null || _j === void 0 ? void 0 : _j.tabButton) !== null && _k !== void 0 ? _k : Types_1.Literals.DefaultTabButtonName;
|
|
913
|
+
ensureNamedImport(j, root, importPath, segNameImport);
|
|
914
|
+
ensureNamedImport(j, root, importPath, tabBtnNameImport);
|
|
915
|
+
ensureNamedImport(j, root, importPath, PATH_PREFIX_DEFAULT_NAME);
|
|
916
|
+
const shouldInjectPressableImport = options.shouldInjectPressableImport !== false;
|
|
917
|
+
if (shouldInjectPressableImport &&
|
|
918
|
+
!hasNamedImportBinding(j, root, 'Pressable')) {
|
|
919
|
+
ensureNamedImport(j, root, '@dynatrace/react-native-plugin/instrumentation/libs/react-native/', 'Pressable');
|
|
920
|
+
}
|
|
921
|
+
const outDir = options.artifactsDir || Types_1.Literals.DefaultArtifactsDir;
|
|
922
|
+
try {
|
|
923
|
+
const rel = path.relative(process.cwd(), state.file);
|
|
924
|
+
const outPath = path.resolve(outDir, rel);
|
|
925
|
+
if (!isPathInside(outDir, outPath)) {
|
|
926
|
+
if (options.log) {
|
|
927
|
+
console.log(`[UIInteractionPlugin]: Skipping artifact outside output dir. file=${state.file} outDir=${outDir}`);
|
|
928
|
+
}
|
|
929
|
+
return { root, changed: !state.skip && state.instrumented };
|
|
930
|
+
}
|
|
931
|
+
const code = root.toSource({ quote: 'single' });
|
|
932
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
933
|
+
fs.writeFileSync(outPath, code, 'utf8');
|
|
934
|
+
}
|
|
935
|
+
catch (e) {
|
|
936
|
+
const errorMsg = e instanceof Error ? e.stack || e.message : String(e);
|
|
937
|
+
if (options.log) {
|
|
938
|
+
console.log(`[UIInteractionPlugin]: Unable to write artifact for ${state.file}: ${errorMsg}`);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
return { root, changed: !state.skip && state.instrumented };
|
|
943
|
+
};
|
|
944
|
+
exports.applyDTUserInteraction = applyDTUserInteraction;
|
|
945
|
+
exports.default = exports.applyDTUserInteraction;
|