@truewatchtech/react-native-mobile-babel-plugin 0.4.3-alpha.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 (80) hide show
  1. package/README.md +185 -0
  2. package/lib/commonjs/actions/global.js +19 -0
  3. package/lib/commonjs/actions/global.js.map +1 -0
  4. package/lib/commonjs/actions/rum/components.js +81 -0
  5. package/lib/commonjs/actions/rum/components.js.map +1 -0
  6. package/lib/commonjs/actions/rum/content.js +168 -0
  7. package/lib/commonjs/actions/rum/content.js.map +1 -0
  8. package/lib/commonjs/actions/rum/index.js +68 -0
  9. package/lib/commonjs/actions/rum/index.js.map +1 -0
  10. package/lib/commonjs/actions/rum/memoization.js +88 -0
  11. package/lib/commonjs/actions/rum/memoization.js.map +1 -0
  12. package/lib/commonjs/actions/rum/metadata.js +63 -0
  13. package/lib/commonjs/actions/rum/metadata.js.map +1 -0
  14. package/lib/commonjs/actions/rum/tap.js +45 -0
  15. package/lib/commonjs/actions/rum/tap.js.map +1 -0
  16. package/lib/commonjs/constants.js +25 -0
  17. package/lib/commonjs/constants.js.map +1 -0
  18. package/lib/commonjs/index.js +48 -0
  19. package/lib/commonjs/index.js.map +1 -0
  20. package/lib/commonjs/options.js +23 -0
  21. package/lib/commonjs/options.js.map +1 -0
  22. package/lib/commonjs/state.js +32 -0
  23. package/lib/commonjs/state.js.map +1 -0
  24. package/lib/commonjs/types.js +6 -0
  25. package/lib/commonjs/types.js.map +1 -0
  26. package/lib/commonjs/utils/jsx.js +43 -0
  27. package/lib/commonjs/utils/jsx.js.map +1 -0
  28. package/lib/module/actions/global.js +13 -0
  29. package/lib/module/actions/global.js.map +1 -0
  30. package/lib/module/actions/rum/components.js +73 -0
  31. package/lib/module/actions/rum/components.js.map +1 -0
  32. package/lib/module/actions/rum/content.js +161 -0
  33. package/lib/module/actions/rum/content.js.map +1 -0
  34. package/lib/module/actions/rum/index.js +61 -0
  35. package/lib/module/actions/rum/index.js.map +1 -0
  36. package/lib/module/actions/rum/memoization.js +82 -0
  37. package/lib/module/actions/rum/memoization.js.map +1 -0
  38. package/lib/module/actions/rum/metadata.js +56 -0
  39. package/lib/module/actions/rum/metadata.js.map +1 -0
  40. package/lib/module/actions/rum/tap.js +38 -0
  41. package/lib/module/actions/rum/tap.js.map +1 -0
  42. package/lib/module/constants.js +19 -0
  43. package/lib/module/constants.js.map +1 -0
  44. package/lib/module/index.js +42 -0
  45. package/lib/module/index.js.map +1 -0
  46. package/lib/module/options.js +17 -0
  47. package/lib/module/options.js.map +1 -0
  48. package/lib/module/state.js +25 -0
  49. package/lib/module/state.js.map +1 -0
  50. package/lib/module/types.js +2 -0
  51. package/lib/module/types.js.map +1 -0
  52. package/lib/module/utils/jsx.js +36 -0
  53. package/lib/module/utils/jsx.js.map +1 -0
  54. package/lib/typescript/actions/global.d.ts +2 -0
  55. package/lib/typescript/actions/rum/components.d.ts +5 -0
  56. package/lib/typescript/actions/rum/content.d.ts +6 -0
  57. package/lib/typescript/actions/rum/index.d.ts +4 -0
  58. package/lib/typescript/actions/rum/memoization.d.ts +4 -0
  59. package/lib/typescript/actions/rum/metadata.d.ts +4 -0
  60. package/lib/typescript/actions/rum/tap.d.ts +6 -0
  61. package/lib/typescript/constants.d.ts +4 -0
  62. package/lib/typescript/index.d.ts +5 -0
  63. package/lib/typescript/options.d.ts +2 -0
  64. package/lib/typescript/state.d.ts +5 -0
  65. package/lib/typescript/types.d.ts +56 -0
  66. package/lib/typescript/utils/jsx.d.ts +4 -0
  67. package/package.json +68 -0
  68. package/src/actions/global.ts +48 -0
  69. package/src/actions/rum/components.ts +128 -0
  70. package/src/actions/rum/content.ts +274 -0
  71. package/src/actions/rum/index.ts +154 -0
  72. package/src/actions/rum/memoization.ts +136 -0
  73. package/src/actions/rum/metadata.ts +147 -0
  74. package/src/actions/rum/tap.ts +148 -0
  75. package/src/constants.ts +21 -0
  76. package/src/index.ts +50 -0
  77. package/src/options.ts +26 -0
  78. package/src/state.ts +39 -0
  79. package/src/types.ts +72 -0
  80. package/src/utils/jsx.ts +50 -0
package/README.md ADDED
@@ -0,0 +1,185 @@
1
+ # TrueWatch React Native Babel plugin
2
+
3
+ This plugin wraps supported React Native interaction handlers at build time so
4
+ the TrueWatch SDK can write a stable, readable value to RUM `action_name`. Use
5
+ the same release version as `@truewatchtech/react-native-mobile`.
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ yarn add -D @truewatchtech/react-native-mobile-babel-plugin
11
+ ```
12
+
13
+ Add the plugin after the React Native preset:
14
+
15
+ ```js
16
+ module.exports = {
17
+ presets: ['module:metro-react-native-babel-preset'],
18
+ plugins: ['@truewatchtech/react-native-mobile-babel-plugin'],
19
+ };
20
+ ```
21
+
22
+ Keep `enableAutoTrackUserAction: true` in `FTRUMConfig`. When the Babel plugin
23
+ is present, the SDK uses Babel-based interaction tracking instead of patching
24
+ React at runtime. Applications that do not install the plugin keep the legacy
25
+ runtime behavior. Both paths call `startAction`; Babel-based handlers use their
26
+ configured `actionType` or `click` by default. Both honor `ft-enable-track` and
27
+ `ft-extra-property` when the interaction event provides a React Native Fiber
28
+ target.
29
+
30
+ ## Action name priority
31
+
32
+ For a tracked component, the action name is selected in this order:
33
+
34
+ 1. Static `ft-action-name` attribute.
35
+ 2. Static custom attribute configured through `actionNameAttribute`.
36
+ 3. Static `accessibilityLabel`.
37
+ 4. Content from `trackingLabel`, `title`, `label`, `text`, a custom content
38
+ prop, or children.
39
+ 5. JSX component name.
40
+
41
+ By default a label is prefixed with the component name, for example
42
+ `Button ("Submit")`.
43
+
44
+ ```tsx
45
+ <Button ft-action-name="checkout" title="Pay now" onPress={submit} />
46
+ ```
47
+
48
+ Content extraction follows Datadog's click-time naming behavior. The generated
49
+ `getContent` closure reads variables and member expressions, evaluates content
50
+ props and children, and extracts text from the resulting React elements. This
51
+ supports dynamic titles, fragments, conditional children, arrays, and zero-argument
52
+ render functions. For example, each invocation of this helper uses its own title:
53
+
54
+ ```tsx
55
+ const renderButton = (title, onPress) => (
56
+ <Pressable onPress={onPress}>
57
+ <Text>{title}</Text>
58
+ </Pressable>
59
+ );
60
+
61
+ renderButton('Bind User', bindUser); // Pressable ("Bind User")
62
+ ```
63
+
64
+ The getter reconstructs content elements with `React.createElement`, preserving
65
+ React 16.13.1 compatibility. It reads click-time values, not a snapshot of the last
66
+ render. Content expressions, including calls and property getters, can therefore
67
+ run again on each tracked interaction. Keep them free of side effects, or provide
68
+ a static `ft-action-name` / `accessibilityLabel` to bypass content extraction.
69
+ `useContent: false` also disables it. Business handlers still run once per call,
70
+ including when content extraction or reporting throws; a content-extraction error
71
+ skips that Action.
72
+
73
+ Render functions with parameters, such as
74
+ `{({ pressed }) => <Text>{title}</Text>}`, are not invoked for naming. Without
75
+ another label, they fall back to the component name (`Pressable`). A zero-argument
76
+ render function can supply text. Spread props are not replayed for naming, and
77
+ props before a spread are ignored because the spread may override them. If no
78
+ non-empty name or content is available, the JSX component name is used.
79
+
80
+ Memoized handlers keep their `useCallback` / `useMemo` identity. If dynamic
81
+ content depends on a narrower scope than the handler definition, such as an
82
+ `item` declared inside `items.map`, the plugin omits that unsafe content getter
83
+ and falls back to the component name. Add a static `ft-action-name` or
84
+ `accessibilityLabel` when that pattern needs a descriptive action name.
85
+
86
+ Upgrade the Babel plugin and rebuild the application to apply these build-time
87
+ changes. Previously transformed bundles keep their existing generated code.
88
+
89
+ ## Custom components
90
+
91
+ All configuration is optional. `useContent` and `useNamePrefix` default to
92
+ `true`; component-level values override the shared defaults.
93
+
94
+ ```ts
95
+ interface PluginOptions {
96
+ actionNameAttribute?: string;
97
+ components?: {
98
+ useContent?: boolean;
99
+ useNamePrefix?: boolean;
100
+ tracked?: Array<{
101
+ name: string;
102
+ useContent?: boolean;
103
+ useNamePrefix?: boolean;
104
+ contentProp?: string;
105
+ handlers: Array<{
106
+ event: string;
107
+ actionType?: string;
108
+ mode?: 'default' | 'delayed';
109
+ }>;
110
+ }>;
111
+ };
112
+ }
113
+ ```
114
+
115
+ ```js
116
+ module.exports = {
117
+ presets: ['module:metro-react-native-babel-preset'],
118
+ plugins: [
119
+ [
120
+ '@truewatchtech/react-native-mobile-babel-plugin',
121
+ {
122
+ actionNameAttribute: 'analytics-name',
123
+ components: {
124
+ useContent: true,
125
+ useNamePrefix: true,
126
+ tracked: [
127
+ {
128
+ name: 'CustomButton',
129
+ contentProp: 'caption',
130
+ handlers: [{ event: 'onPress', actionType: 'submit' }],
131
+ },
132
+ ],
133
+ },
134
+ },
135
+ ],
136
+ ],
137
+ };
138
+ ```
139
+
140
+ The plugin automatically tracks named imports of `Button`, `Pressable`, the
141
+ Touchable components, `Switch`, and `TextInput` from `react-native`. Import
142
+ aliases are supported. Namespace imports are not auto-discovered, but their
143
+ full JSX names can be configured through `components.tracked`.
144
+
145
+ `actionType` is written to RUM `action_type` through `startAction`; it defaults
146
+ to `click` when omitted, empty, or whitespace-only. Keep custom values stable and
147
+ low-cardinality. Explicit action-name attributes must be static strings; content
148
+ props and children may be expressions. Web builds and files under `node_modules`
149
+ are left unchanged. A handler configured with `mode: 'delayed'` is treated as a
150
+ factory whose return value is the actual interaction handler. Direct arrow
151
+ functions, function expressions, identifiers, member expressions, and conditional
152
+ expressions are supported.
153
+
154
+ ## Development
155
+
156
+ The plugin is organized by responsibility:
157
+
158
+ ```text
159
+ src/
160
+ index.ts Plugin declaration and visitor orchestration
161
+ constants.ts Runtime names, attributes, and supported native events
162
+ options.ts Configuration defaults
163
+ types.ts Public options and internal transform types
164
+ state.ts File filtering and per-file state initialization
165
+ actions/
166
+ global.ts Compatible plugin-enabled flag injection
167
+ rum/
168
+ index.ts JSX action processing and runtime imports
169
+ components.ts Component discovery and required TextInput handlers
170
+ content.ts JSX conversion and click-time content getters
171
+ metadata.ts Action-name attributes and runtime target objects
172
+ tap.ts Immediate, delayed, and conditional handler wrappers
173
+ memoization.ts useCallback / useMemo handling
174
+ utils/
175
+ jsx.ts JSX name resolution and attribute paths
176
+ ```
177
+
178
+ The visitor initializes state for each file, delegates tracked JSX elements to
179
+ `actions/rum`, and inserts the required runtime imports on program exit. Runtime
180
+ action-name selection and reporting live in `@truewatchtech/react-native-mobile`.
181
+ The package entry continues to export the plugin and its public option types.
182
+
183
+ ## License
184
+
185
+ Apache 2.0
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.insertPluginEnabledFlag = insertPluginEnabledFlag;
7
+ var _constants = require("../constants");
8
+ /*
9
+ * Portions of this file are adapted from Datadog's dd-sdk-reactnative Babel
10
+ * interaction tracking implementation, licensed under Apache-2.0.
11
+ */
12
+
13
+ function insertPluginEnabledFlag(t, programPath) {
14
+ const createFlagAssignment = globalIdentifier => t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.cloneNode(globalIdentifier), t.identifier(_constants.PLUGIN_ENABLED_FLAG)), t.booleanLiteral(true)));
15
+ const globalThisIdentifier = t.identifier('globalThis');
16
+ const reactNativeGlobalIdentifier = t.identifier('global');
17
+ programPath.unshiftContainer('body', t.ifStatement(t.binaryExpression('!==', t.unaryExpression('typeof', t.cloneNode(globalThisIdentifier)), t.stringLiteral('undefined')), t.blockStatement([createFlagAssignment(globalThisIdentifier)]), t.ifStatement(t.binaryExpression('!==', t.unaryExpression('typeof', t.cloneNode(reactNativeGlobalIdentifier)), t.stringLiteral('undefined')), t.blockStatement([createFlagAssignment(reactNativeGlobalIdentifier)]))));
18
+ }
19
+ //# sourceMappingURL=global.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_constants","require","insertPluginEnabledFlag","t","programPath","createFlagAssignment","globalIdentifier","expressionStatement","assignmentExpression","memberExpression","cloneNode","identifier","PLUGIN_ENABLED_FLAG","booleanLiteral","globalThisIdentifier","reactNativeGlobalIdentifier","unshiftContainer","ifStatement","binaryExpression","unaryExpression","stringLiteral","blockStatement"],"sources":["global.ts"],"sourcesContent":["/*\n * Portions of this file are adapted from Datadog's dd-sdk-reactnative Babel\n * interaction tracking implementation, licensed under Apache-2.0.\n */\n\nimport type * as Babel from '@babel/core';\nimport { PLUGIN_ENABLED_FLAG } from '../constants';\n\nexport function insertPluginEnabledFlag(\n t: typeof Babel.types,\n programPath: Babel.NodePath<Babel.types.Program>\n): void {\n const createFlagAssignment = (\n globalIdentifier: Babel.types.Identifier\n ): Babel.types.ExpressionStatement =>\n t.expressionStatement(\n t.assignmentExpression(\n '=',\n t.memberExpression(\n t.cloneNode(globalIdentifier),\n t.identifier(PLUGIN_ENABLED_FLAG)\n ),\n t.booleanLiteral(true)\n )\n );\n const globalThisIdentifier = t.identifier('globalThis');\n const reactNativeGlobalIdentifier = t.identifier('global');\n\n programPath.unshiftContainer(\n 'body',\n t.ifStatement(\n t.binaryExpression(\n '!==',\n t.unaryExpression('typeof', t.cloneNode(globalThisIdentifier)),\n t.stringLiteral('undefined')\n ),\n t.blockStatement([createFlagAssignment(globalThisIdentifier)]),\n t.ifStatement(\n t.binaryExpression(\n '!==',\n t.unaryExpression('typeof', t.cloneNode(reactNativeGlobalIdentifier)),\n t.stringLiteral('undefined')\n ),\n t.blockStatement([createFlagAssignment(reactNativeGlobalIdentifier)])\n )\n )\n );\n}\n"],"mappings":";;;;;;AAMA,IAAAA,UAAA,GAAAC,OAAA;AANA;AACA;AACA;AACA;;AAKO,SAASC,uBAAuBA,CACrCC,CAAqB,EACrBC,WAAgD,EAC1C;EACN,MAAMC,oBAAoB,GACxBC,gBAAwC,IAExCH,CAAC,CAACI,mBAAmB,CACnBJ,CAAC,CAACK,oBAAoB,CACpB,GAAG,EACHL,CAAC,CAACM,gBAAgB,CAChBN,CAAC,CAACO,SAAS,CAACJ,gBAAgB,CAAC,EAC7BH,CAAC,CAACQ,UAAU,CAACC,8BAAmB,CAClC,CAAC,EACDT,CAAC,CAACU,cAAc,CAAC,IAAI,CACvB,CACF,CAAC;EACH,MAAMC,oBAAoB,GAAGX,CAAC,CAACQ,UAAU,CAAC,YAAY,CAAC;EACvD,MAAMI,2BAA2B,GAAGZ,CAAC,CAACQ,UAAU,CAAC,QAAQ,CAAC;EAE1DP,WAAW,CAACY,gBAAgB,CAC1B,MAAM,EACNb,CAAC,CAACc,WAAW,CACXd,CAAC,CAACe,gBAAgB,CAChB,KAAK,EACLf,CAAC,CAACgB,eAAe,CAAC,QAAQ,EAAEhB,CAAC,CAACO,SAAS,CAACI,oBAAoB,CAAC,CAAC,EAC9DX,CAAC,CAACiB,aAAa,CAAC,WAAW,CAC7B,CAAC,EACDjB,CAAC,CAACkB,cAAc,CAAC,CAAChB,oBAAoB,CAACS,oBAAoB,CAAC,CAAC,CAAC,EAC9DX,CAAC,CAACc,WAAW,CACXd,CAAC,CAACe,gBAAgB,CAChB,KAAK,EACLf,CAAC,CAACgB,eAAe,CAAC,QAAQ,EAAEhB,CAAC,CAACO,SAAS,CAACK,2BAA2B,CAAC,CAAC,EACrEZ,CAAC,CAACiB,aAAa,CAAC,WAAW,CAC7B,CAAC,EACDjB,CAAC,CAACkB,cAAc,CAAC,CAAChB,oBAAoB,CAACU,2BAA2B,CAAC,CAAC,CACtE,CACF,CACF,CAAC;AACH","ignoreList":[]}
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.addRequiredTextInputHandler = addRequiredTextInputHandler;
7
+ exports.createTrackedComponents = createTrackedComponents;
8
+ exports.isInsideConfiguredComponent = isInsideConfiguredComponent;
9
+ var _constants = require("../../constants");
10
+ var _jsx = require("../../utils/jsx");
11
+ /*
12
+ * Portions of this file are adapted from Datadog's dd-sdk-reactnative Babel
13
+ * interaction tracking implementation, licensed under Apache-2.0.
14
+ */
15
+
16
+ function createTrackedComponents(t, programPath, options) {
17
+ const tracked = {};
18
+ for (const statement of programPath.node.body) {
19
+ if (!t.isImportDeclaration(statement) || statement.source.value !== 'react-native') {
20
+ continue;
21
+ }
22
+ for (const specifier of statement.specifiers) {
23
+ if (!t.isImportSpecifier(specifier)) {
24
+ continue;
25
+ }
26
+ const importedName = (0, _jsx.getNodeName)(t, specifier.imported);
27
+ const localName = (0, _jsx.getNodeName)(t, specifier.local);
28
+ const events = importedName ? _constants.NATIVE_COMPONENT_HANDLERS[importedName] : undefined;
29
+ if (!events || !importedName || !localName) {
30
+ continue;
31
+ }
32
+ tracked[localName] = {
33
+ handlers: events.map(event => ({
34
+ event
35
+ })),
36
+ importedName,
37
+ isCustom: false,
38
+ useContent: options.components.useContent,
39
+ useNamePrefix: options.components.useNamePrefix
40
+ };
41
+ }
42
+ }
43
+ for (const component of options.components.tracked) {
44
+ tracked[component.name] = {
45
+ ...(component.contentProp ? {
46
+ contentProp: component.contentProp
47
+ } : {}),
48
+ handlers: component.handlers || [],
49
+ isCustom: true,
50
+ useContent: component.useContent === undefined ? options.components.useContent : component.useContent,
51
+ useNamePrefix: component.useNamePrefix === undefined ? options.components.useNamePrefix : component.useNamePrefix
52
+ };
53
+ }
54
+ return tracked;
55
+ }
56
+ function isInsideConfiguredComponent(t, path, customComponentNames) {
57
+ let current = path.parentPath;
58
+ while (current && !current.isProgram()) {
59
+ if (current.isFunctionDeclaration() || current.isClassDeclaration() || current.isVariableDeclarator()) {
60
+ const name = (0, _jsx.getNodeName)(t, current.node.id);
61
+ if (name && customComponentNames.has(name)) {
62
+ return true;
63
+ }
64
+ }
65
+ current = current.parentPath;
66
+ }
67
+ return false;
68
+ }
69
+ function addRequiredTextInputHandler(t, path, component, componentName) {
70
+ if (component.isCustom || component.importedName !== 'TextInput' || componentName !== 'TextInput') {
71
+ return;
72
+ }
73
+ const attributes = (0, _jsx.getDirectAttributePaths)(path);
74
+ const hasSpread = attributes.some(attribute => attribute.isJSXSpreadAttribute());
75
+ const hasOnFocus = attributes.some(attribute => attribute.isJSXAttribute() && (0, _jsx.getNodeName)(t, attribute.node.name) === 'onFocus');
76
+ if (!hasSpread && !hasOnFocus) {
77
+ const openingElementPath = path.get('openingElement');
78
+ openingElementPath.pushContainer('attributes', t.jsxAttribute(t.jsxIdentifier('onFocus'), t.jsxExpressionContainer(t.arrowFunctionExpression([], t.blockStatement([])))));
79
+ }
80
+ }
81
+ //# sourceMappingURL=components.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_constants","require","_jsx","createTrackedComponents","t","programPath","options","tracked","statement","node","body","isImportDeclaration","source","value","specifier","specifiers","isImportSpecifier","importedName","getNodeName","imported","localName","local","events","NATIVE_COMPONENT_HANDLERS","undefined","handlers","map","event","isCustom","useContent","components","useNamePrefix","component","name","contentProp","isInsideConfiguredComponent","path","customComponentNames","current","parentPath","isProgram","isFunctionDeclaration","isClassDeclaration","isVariableDeclarator","id","has","addRequiredTextInputHandler","componentName","attributes","getDirectAttributePaths","hasSpread","some","attribute","isJSXSpreadAttribute","hasOnFocus","isJSXAttribute","openingElementPath","get","pushContainer","jsxAttribute","jsxIdentifier","jsxExpressionContainer","arrowFunctionExpression","blockStatement"],"sources":["components.ts"],"sourcesContent":["/*\n * Portions of this file are adapted from Datadog's dd-sdk-reactnative Babel\n * interaction tracking implementation, licensed under Apache-2.0.\n */\n\nimport type * as Babel from '@babel/core';\nimport { NATIVE_COMPONENT_HANDLERS } from '../../constants';\nimport type {\n NormalizedPluginOptions,\n TrackedComponentData,\n} from '../../types';\nimport { getDirectAttributePaths, getNodeName } from '../../utils/jsx';\n\nexport function createTrackedComponents(\n t: typeof Babel.types,\n programPath: Babel.NodePath<Babel.types.Program>,\n options: NormalizedPluginOptions\n): Record<string, TrackedComponentData> {\n const tracked: Record<string, TrackedComponentData> = {};\n\n for (const statement of programPath.node.body) {\n if (\n !t.isImportDeclaration(statement) ||\n statement.source.value !== 'react-native'\n ) {\n continue;\n }\n\n for (const specifier of statement.specifiers) {\n if (!t.isImportSpecifier(specifier)) {\n continue;\n }\n const importedName = getNodeName(t, specifier.imported);\n const localName = getNodeName(t, specifier.local);\n const events = importedName\n ? NATIVE_COMPONENT_HANDLERS[importedName]\n : undefined;\n if (!events || !importedName || !localName) {\n continue;\n }\n tracked[localName] = {\n handlers: events.map((event) => ({ event })),\n importedName,\n isCustom: false,\n useContent: options.components.useContent,\n useNamePrefix: options.components.useNamePrefix,\n };\n }\n }\n\n for (const component of options.components.tracked) {\n tracked[component.name] = {\n ...(component.contentProp ? { contentProp: component.contentProp } : {}),\n handlers: component.handlers || [],\n isCustom: true,\n useContent:\n component.useContent === undefined\n ? options.components.useContent\n : component.useContent,\n useNamePrefix:\n component.useNamePrefix === undefined\n ? options.components.useNamePrefix\n : component.useNamePrefix,\n };\n }\n\n return tracked;\n}\n\nexport function isInsideConfiguredComponent(\n t: typeof Babel.types,\n path: Babel.NodePath<Babel.types.JSXElement>,\n customComponentNames: Set<string>\n): boolean {\n let current: Babel.NodePath | null = path.parentPath;\n while (current && !current.isProgram()) {\n if (\n current.isFunctionDeclaration() ||\n current.isClassDeclaration() ||\n current.isVariableDeclarator()\n ) {\n const name = getNodeName(t, current.node.id);\n if (name && customComponentNames.has(name)) {\n return true;\n }\n }\n current = current.parentPath;\n }\n return false;\n}\n\nexport function addRequiredTextInputHandler(\n t: typeof Babel.types,\n path: Babel.NodePath<Babel.types.JSXElement>,\n component: TrackedComponentData,\n componentName: string\n): void {\n if (\n component.isCustom ||\n component.importedName !== 'TextInput' ||\n componentName !== 'TextInput'\n ) {\n return;\n }\n const attributes = getDirectAttributePaths(path);\n const hasSpread = attributes.some((attribute) =>\n attribute.isJSXSpreadAttribute()\n );\n const hasOnFocus = attributes.some(\n (attribute) =>\n attribute.isJSXAttribute() &&\n getNodeName(t, attribute.node.name) === 'onFocus'\n );\n if (!hasSpread && !hasOnFocus) {\n const openingElementPath = path.get(\n 'openingElement'\n ) as Babel.NodePath<Babel.types.JSXOpeningElement>;\n openingElementPath.pushContainer(\n 'attributes',\n t.jsxAttribute(\n t.jsxIdentifier('onFocus'),\n t.jsxExpressionContainer(\n t.arrowFunctionExpression([], t.blockStatement([]))\n )\n )\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.buildContentGetter = buildContentGetter;
7
+ exports.isContentVisibleFromScope = isContentVisibleFromScope;
8
+ var _jsx = require("../../utils/jsx");
9
+ /*
10
+ * Portions of this file are adapted from Datadog's dd-sdk-reactnative Babel
11
+ * interaction tracking implementation, licensed under Apache-2.0.
12
+ */
13
+
14
+ const CONTENT_PROPS = ['trackingLabel', 'title', 'label', 'text'];
15
+ function contentCandidateNames(component) {
16
+ return component.contentProp && !CONTENT_PROPS.includes(component.contentProp) ? [...CONTENT_PROPS, component.contentProp] : CONTENT_PROPS;
17
+ }
18
+ function elementType(t, name) {
19
+ if (t.isJSXMemberExpression(name)) {
20
+ // Lowercase member receivers (e.g. ui.Text) are variables, not host tags.
21
+ const receiver = t.isJSXIdentifier(name.object) ? t.identifier(name.object.name) : elementType(t, name.object);
22
+ return t.memberExpression(receiver, t.identifier(name.property.name));
23
+ }
24
+ const value = (0, _jsx.getNodeName)(t, name) || '';
25
+ return t.isJSXIdentifier(name) && !/^[a-z]/.test(value) ? t.identifier(value) : t.stringLiteral(value);
26
+ }
27
+ function effectiveAttributes(t, attributes) {
28
+ const result = new Map();
29
+ for (const attribute of attributes) {
30
+ if (t.isJSXSpreadAttribute(attribute)) {
31
+ // Do not replay spreads or trust props they may have overridden.
32
+ result.clear();
33
+ } else {
34
+ result.set((0, _jsx.getNodeName)(t, attribute.name) || '', attribute);
35
+ }
36
+ }
37
+ return result;
38
+ }
39
+ function contentSourcePaths(t, path, component) {
40
+ const candidateNames = contentCandidateNames(component);
41
+ const attributes = new Map();
42
+ for (const attributePath of (0, _jsx.getDirectAttributePaths)(path)) {
43
+ if (!attributePath.isJSXAttribute()) {
44
+ attributes.clear();
45
+ continue;
46
+ }
47
+ attributes.set((0, _jsx.getNodeName)(t, attributePath.node.name) || '', attributePath);
48
+ }
49
+ const sources = [];
50
+ for (const name of candidateNames) {
51
+ const attributePath = attributes.get(name);
52
+ const valuePath = attributePath === null || attributePath === void 0 ? void 0 : attributePath.get('value');
53
+ if (valuePath && !Array.isArray(valuePath) && valuePath.node) {
54
+ sources.push(valuePath);
55
+ }
56
+ }
57
+ return sources.concat(path.get('children'));
58
+ }
59
+ function containsPath(root, candidate) {
60
+ let current = candidate;
61
+ while (current) {
62
+ if (current === root || current.node === root.node) {
63
+ return true;
64
+ }
65
+ current = current.parentPath;
66
+ }
67
+ return false;
68
+ }
69
+
70
+ /** Checks whether moving content into a memo callback preserves every binding. */
71
+ function isContentVisibleFromScope(t, path, component, destinationScope) {
72
+ const sources = contentSourcePaths(t, path, component);
73
+ let visible = true;
74
+ const checkReference = (referencePath, name) => {
75
+ const sourceBinding = referencePath.scope.getBinding(name);
76
+ if (sourceBinding && sources.some(source => containsPath(source, sourceBinding.path))) {
77
+ return;
78
+ }
79
+ const destinationBinding = destinationScope.getBinding(name);
80
+ if ((sourceBinding === null || sourceBinding === void 0 ? void 0 : sourceBinding.identifier) !== (destinationBinding === null || destinationBinding === void 0 ? void 0 : destinationBinding.identifier)) {
81
+ visible = false;
82
+ }
83
+ };
84
+ for (const source of sources) {
85
+ source.traverse({
86
+ ReferencedIdentifier(referencePath) {
87
+ checkReference(referencePath, referencePath.node.name);
88
+ if (!visible) {
89
+ referencePath.stop();
90
+ }
91
+ },
92
+ JSXOpeningElement(openingPath) {
93
+ let name = openingPath.node.name;
94
+ let isMember = false;
95
+ while (t.isJSXMemberExpression(name)) {
96
+ isMember = true;
97
+ name = name.object;
98
+ }
99
+ if (t.isJSXIdentifier(name) && (isMember || !/^[a-z]/.test(name.name))) {
100
+ checkReference(openingPath, name.name);
101
+ }
102
+ if (!visible) {
103
+ openingPath.stop();
104
+ }
105
+ },
106
+ JSXSpreadAttribute(spreadPath) {
107
+ // Spread attributes are not copied into the content getter.
108
+ spreadPath.skip();
109
+ }
110
+ });
111
+ if (!visible) {
112
+ return false;
113
+ }
114
+ }
115
+ return true;
116
+ }
117
+
118
+ /** Converts JSX anywhere in an expression, including render functions and maps. */
119
+ function toRuntimeNode(t, node, react) {
120
+ if (t.isJSXElement(node) || t.isJSXFragment(node)) {
121
+ const properties = [];
122
+ if (t.isJSXElement(node)) {
123
+ for (const [name, attribute] of effectiveAttributes(t, node.openingElement.attributes)) {
124
+ properties.push(t.objectProperty(t.stringLiteral(name), attribute.value ? toRuntimeNode(t, attribute.value, react) : t.booleanLiteral(true)));
125
+ }
126
+ }
127
+ const children = node.children.filter(child => !(t.isJSXText(child) && !child.value.trim()) && !(t.isJSXExpressionContainer(child) && t.isJSXEmptyExpression(child.expression))).map(child => toRuntimeNode(t, child, react));
128
+ return t.callExpression(t.memberExpression(t.cloneNode(react), t.identifier('createElement')), [t.isJSXElement(node) ? elementType(t, node.openingElement.name) : t.memberExpression(t.cloneNode(react), t.identifier('Fragment')), properties.length ? t.objectExpression(properties) : t.nullLiteral(), ...children]);
129
+ }
130
+ if (t.isJSXText(node)) {
131
+ return t.stringLiteral(node.value.replace(/\s+/g, ' ').trim());
132
+ }
133
+ if (t.isJSXExpressionContainer(node)) {
134
+ return toRuntimeNode(t, node.expression, react);
135
+ }
136
+ // Recurse through all expression shapes so cloned JSX cannot survive Babel's
137
+ // JSX visitor or be instrumented a second time inside the content getter.
138
+ const cloned = t.cloneNode(node, false);
139
+ const fields = cloned;
140
+ for (const key of t.VISITOR_KEYS[node.type] || []) {
141
+ const value = fields[key];
142
+ if (Array.isArray(value)) {
143
+ fields[key] = value.map(child => child ? toRuntimeNode(t, child, react) : child);
144
+ } else if (value) {
145
+ fields[key] = toRuntimeNode(t, value, react);
146
+ }
147
+ }
148
+ return cloned;
149
+ }
150
+
151
+ /** Matches Datadog's click-time evaluation of content props and JSX children. */
152
+ function buildContentGetter(t, path, component, state) {
153
+ if (!component.useContent) {
154
+ return null;
155
+ }
156
+ const candidateNames = contentCandidateNames(component);
157
+ const attributes = effectiveAttributes(t, path.node.openingElement.attributes);
158
+ const candidates = [];
159
+ for (const name of candidateNames) {
160
+ const attribute = attributes.get(name);
161
+ if (attribute !== null && attribute !== void 0 && attribute.value) {
162
+ candidates.push(toRuntimeNode(t, attribute.value, state._ftReactIdentifier));
163
+ }
164
+ }
165
+ const fragment = t.jsxFragment(t.jsxOpeningFragment(), t.jsxClosingFragment(), path.node.children);
166
+ return t.arrowFunctionExpression([], t.callExpression(t.cloneNode(state._ftExtractTextIdentifier), [toRuntimeNode(t, fragment, state._ftReactIdentifier), t.arrayExpression(candidates)]));
167
+ }
168
+ //# sourceMappingURL=content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_jsx","require","CONTENT_PROPS","contentCandidateNames","component","contentProp","includes","elementType","t","name","isJSXMemberExpression","receiver","isJSXIdentifier","object","identifier","memberExpression","property","value","getNodeName","test","stringLiteral","effectiveAttributes","attributes","result","Map","attribute","isJSXSpreadAttribute","clear","set","contentSourcePaths","path","candidateNames","attributePath","getDirectAttributePaths","isJSXAttribute","node","sources","get","valuePath","Array","isArray","push","concat","containsPath","root","candidate","current","parentPath","isContentVisibleFromScope","destinationScope","visible","checkReference","referencePath","sourceBinding","scope","getBinding","some","source","destinationBinding","traverse","ReferencedIdentifier","stop","JSXOpeningElement","openingPath","isMember","JSXSpreadAttribute","spreadPath","skip","toRuntimeNode","react","isJSXElement","isJSXFragment","properties","openingElement","objectProperty","booleanLiteral","children","filter","child","isJSXText","trim","isJSXExpressionContainer","isJSXEmptyExpression","expression","map","callExpression","cloneNode","length","objectExpression","nullLiteral","replace","cloned","fields","key","VISITOR_KEYS","type","buildContentGetter","state","useContent","candidates","_ftReactIdentifier","fragment","jsxFragment","jsxOpeningFragment","jsxClosingFragment","arrowFunctionExpression","_ftExtractTextIdentifier","arrayExpression"],"sources":["content.ts"],"sourcesContent":["/*\n * Portions of this file are adapted from Datadog's dd-sdk-reactnative Babel\n * interaction tracking implementation, licensed under Apache-2.0.\n */\n\nimport type * as Babel from '@babel/core';\nimport type { PluginState, TrackedComponentData } from '../../types';\nimport { getDirectAttributePaths, getNodeName } from '../../utils/jsx';\n\nconst CONTENT_PROPS = ['trackingLabel', 'title', 'label', 'text'];\n\nfunction contentCandidateNames(component: TrackedComponentData): string[] {\n return component.contentProp && !CONTENT_PROPS.includes(component.contentProp)\n ? [...CONTENT_PROPS, component.contentProp]\n : CONTENT_PROPS;\n}\n\nfunction elementType(\n t: typeof Babel.types,\n name: Babel.types.JSXOpeningElement['name']\n): Babel.types.Expression {\n if (t.isJSXMemberExpression(name)) {\n // Lowercase member receivers (e.g. ui.Text) are variables, not host tags.\n const receiver = t.isJSXIdentifier(name.object)\n ? t.identifier(name.object.name)\n : elementType(t, name.object);\n return t.memberExpression(receiver, t.identifier(name.property.name));\n }\n const value = getNodeName(t, name) || '';\n return t.isJSXIdentifier(name) && !/^[a-z]/.test(value)\n ? t.identifier(value)\n : t.stringLiteral(value);\n}\n\nfunction effectiveAttributes(\n t: typeof Babel.types,\n attributes: Babel.types.JSXOpeningElement['attributes']\n): Map<string, Babel.types.JSXAttribute> {\n const result = new Map<string, Babel.types.JSXAttribute>();\n for (const attribute of attributes) {\n if (t.isJSXSpreadAttribute(attribute)) {\n // Do not replay spreads or trust props they may have overridden.\n result.clear();\n } else {\n result.set(getNodeName(t, attribute.name) || '', attribute);\n }\n }\n return result;\n}\n\nfunction contentSourcePaths(\n t: typeof Babel.types,\n path: Babel.NodePath<Babel.types.JSXElement>,\n component: TrackedComponentData\n): Babel.NodePath[] {\n const candidateNames = contentCandidateNames(component);\n const attributes = new Map<\n string,\n Babel.NodePath<Babel.types.JSXAttribute>\n >();\n for (const attributePath of getDirectAttributePaths(path)) {\n if (!attributePath.isJSXAttribute()) {\n attributes.clear();\n continue;\n }\n attributes.set(\n getNodeName(t, attributePath.node.name) || '',\n attributePath\n );\n }\n const sources: Babel.NodePath[] = [];\n for (const name of candidateNames) {\n const attributePath = attributes.get(name);\n const valuePath = attributePath?.get('value');\n if (valuePath && !Array.isArray(valuePath) && valuePath.node) {\n sources.push(valuePath as Babel.NodePath<Babel.types.Node>);\n }\n }\n return sources.concat(path.get('children') as Babel.NodePath[]);\n}\n\nfunction containsPath(\n root: Babel.NodePath,\n candidate: Babel.NodePath\n): boolean {\n let current: Babel.NodePath | null = candidate;\n while (current) {\n if (current === root || current.node === root.node) {\n return true;\n }\n current = current.parentPath;\n }\n return false;\n}\n\n/** Checks whether moving content into a memo callback preserves every binding. */\nexport function isContentVisibleFromScope(\n t: typeof Babel.types,\n path: Babel.NodePath<Babel.types.JSXElement>,\n component: TrackedComponentData,\n destinationScope: Babel.NodePath['scope']\n): boolean {\n const sources = contentSourcePaths(t, path, component);\n let visible = true;\n const checkReference = (referencePath: Babel.NodePath, name: string) => {\n const sourceBinding = referencePath.scope.getBinding(name);\n if (\n sourceBinding &&\n sources.some((source) => containsPath(source, sourceBinding.path))\n ) {\n return;\n }\n const destinationBinding = destinationScope.getBinding(name);\n if (sourceBinding?.identifier !== destinationBinding?.identifier) {\n visible = false;\n }\n };\n\n for (const source of sources) {\n source.traverse({\n ReferencedIdentifier(referencePath) {\n checkReference(referencePath, referencePath.node.name);\n if (!visible) {\n referencePath.stop();\n }\n },\n JSXOpeningElement(openingPath) {\n let name = openingPath.node.name;\n let isMember = false;\n while (t.isJSXMemberExpression(name)) {\n isMember = true;\n name = name.object;\n }\n if (\n t.isJSXIdentifier(name) &&\n (isMember || !/^[a-z]/.test(name.name))\n ) {\n checkReference(openingPath, name.name);\n }\n if (!visible) {\n openingPath.stop();\n }\n },\n JSXSpreadAttribute(spreadPath) {\n // Spread attributes are not copied into the content getter.\n spreadPath.skip();\n },\n });\n if (!visible) {\n return false;\n }\n }\n return true;\n}\n\n/** Converts JSX anywhere in an expression, including render functions and maps. */\nfunction toRuntimeNode(\n t: typeof Babel.types,\n node: Babel.types.Node,\n react: Babel.types.Identifier\n): Babel.types.Node {\n if (t.isJSXElement(node) || t.isJSXFragment(node)) {\n const properties: Babel.types.ObjectProperty[] = [];\n if (t.isJSXElement(node)) {\n for (const [name, attribute] of effectiveAttributes(\n t,\n node.openingElement.attributes\n )) {\n properties.push(\n t.objectProperty(\n t.stringLiteral(name),\n attribute.value\n ? (toRuntimeNode(\n t,\n attribute.value,\n react\n ) as Babel.types.Expression)\n : t.booleanLiteral(true)\n )\n );\n }\n }\n const children = node.children\n .filter(\n (child) =>\n !(t.isJSXText(child) && !child.value.trim()) &&\n !(\n t.isJSXExpressionContainer(child) &&\n t.isJSXEmptyExpression(child.expression)\n )\n )\n .map((child) => toRuntimeNode(t, child, react) as Babel.types.Expression);\n return t.callExpression(\n t.memberExpression(t.cloneNode(react), t.identifier('createElement')),\n [\n t.isJSXElement(node)\n ? elementType(t, node.openingElement.name)\n : t.memberExpression(t.cloneNode(react), t.identifier('Fragment')),\n properties.length ? t.objectExpression(properties) : t.nullLiteral(),\n ...children,\n ]\n );\n }\n if (t.isJSXText(node)) {\n return t.stringLiteral(node.value.replace(/\\s+/g, ' ').trim());\n }\n if (t.isJSXExpressionContainer(node)) {\n return toRuntimeNode(t, node.expression, react);\n }\n // Recurse through all expression shapes so cloned JSX cannot survive Babel's\n // JSX visitor or be instrumented a second time inside the content getter.\n const cloned = t.cloneNode(node, false);\n const fields = cloned as unknown as Record<\n string,\n Babel.types.Node | Babel.types.Node[] | null\n >;\n for (const key of t.VISITOR_KEYS[node.type] || []) {\n const value = fields[key];\n if (Array.isArray(value)) {\n fields[key] = value.map((child) =>\n child ? toRuntimeNode(t, child, react) : child\n );\n } else if (value) {\n fields[key] = toRuntimeNode(t, value, react);\n }\n }\n return cloned;\n}\n\n/** Matches Datadog's click-time evaluation of content props and JSX children. */\nexport function buildContentGetter(\n t: typeof Babel.types,\n path: Babel.NodePath<Babel.types.JSXElement>,\n component: TrackedComponentData,\n state: PluginState\n): Babel.types.ArrowFunctionExpression | null {\n if (!component.useContent) {\n return null;\n }\n const candidateNames = contentCandidateNames(component);\n const attributes = effectiveAttributes(\n t,\n path.node.openingElement.attributes\n );\n const candidates: Babel.types.Expression[] = [];\n for (const name of candidateNames) {\n const attribute = attributes.get(name);\n if (attribute?.value) {\n candidates.push(\n toRuntimeNode(\n t,\n attribute.value,\n state._ftReactIdentifier\n ) as Babel.types.Expression\n );\n }\n }\n const fragment = t.jsxFragment(\n t.jsxOpeningFragment(),\n t.jsxClosingFragment(),\n path.node.children\n );\n return t.arrowFunctionExpression(\n [],\n t.callExpression(t.cloneNode(state._ftExtractTextIdentifier), [\n toRuntimeNode(\n t,\n fragment,\n state._ftReactIdentifier\n ) as Babel.types.Expression,\n t.arrayExpression(candidates),\n ])\n );\n}\n"],"mappings":";;;;;;;AAOA,IAAAA,IAAA,GAAAC,OAAA;AAPA;AACA;AACA;AACA;;AAMA,MAAMC,aAAa,GAAG,CAAC,eAAe,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC;AAEjE,SAASC,qBAAqBA,CAACC,SAA+B,EAAY;EACxE,OAAOA,SAAS,CAACC,WAAW,IAAI,CAACH,aAAa,CAACI,QAAQ,CAACF,SAAS,CAACC,WAAW,CAAC,GAC1E,CAAC,GAAGH,aAAa,EAAEE,SAAS,CAACC,WAAW,CAAC,GACzCH,aAAa;AACnB;AAEA,SAASK,WAAWA,CAClBC,CAAqB,EACrBC,IAA2C,EACnB;EACxB,IAAID,CAAC,CAACE,qBAAqB,CAACD,IAAI,CAAC,EAAE;IACjC;IACA,MAAME,QAAQ,GAAGH,CAAC,CAACI,eAAe,CAACH,IAAI,CAACI,MAAM,CAAC,GAC3CL,CAAC,CAACM,UAAU,CAACL,IAAI,CAACI,MAAM,CAACJ,IAAI,CAAC,GAC9BF,WAAW,CAACC,CAAC,EAAEC,IAAI,CAACI,MAAM,CAAC;IAC/B,OAAOL,CAAC,CAACO,gBAAgB,CAACJ,QAAQ,EAAEH,CAAC,CAACM,UAAU,CAACL,IAAI,CAACO,QAAQ,CAACP,IAAI,CAAC,CAAC;EACvE;EACA,MAAMQ,KAAK,GAAG,IAAAC,gBAAW,EAACV,CAAC,EAAEC,IAAI,CAAC,IAAI,EAAE;EACxC,OAAOD,CAAC,CAACI,eAAe,CAACH,IAAI,CAAC,IAAI,CAAC,QAAQ,CAACU,IAAI,CAACF,KAAK,CAAC,GACnDT,CAAC,CAACM,UAAU,CAACG,KAAK,CAAC,GACnBT,CAAC,CAACY,aAAa,CAACH,KAAK,CAAC;AAC5B;AAEA,SAASI,mBAAmBA,CAC1Bb,CAAqB,EACrBc,UAAuD,EAChB;EACvC,MAAMC,MAAM,GAAG,IAAIC,GAAG,CAAmC,CAAC;EAC1D,KAAK,MAAMC,SAAS,IAAIH,UAAU,EAAE;IAClC,IAAId,CAAC,CAACkB,oBAAoB,CAACD,SAAS,CAAC,EAAE;MACrC;MACAF,MAAM,CAACI,KAAK,CAAC,CAAC;IAChB,CAAC,MAAM;MACLJ,MAAM,CAACK,GAAG,CAAC,IAAAV,gBAAW,EAACV,CAAC,EAAEiB,SAAS,CAAChB,IAAI,CAAC,IAAI,EAAE,EAAEgB,SAAS,CAAC;IAC7D;EACF;EACA,OAAOF,MAAM;AACf;AAEA,SAASM,kBAAkBA,CACzBrB,CAAqB,EACrBsB,IAA4C,EAC5C1B,SAA+B,EACb;EAClB,MAAM2B,cAAc,GAAG5B,qBAAqB,CAACC,SAAS,CAAC;EACvD,MAAMkB,UAAU,GAAG,IAAIE,GAAG,CAGxB,CAAC;EACH,KAAK,MAAMQ,aAAa,IAAI,IAAAC,4BAAuB,EAACH,IAAI,CAAC,EAAE;IACzD,IAAI,CAACE,aAAa,CAACE,cAAc,CAAC,CAAC,EAAE;MACnCZ,UAAU,CAACK,KAAK,CAAC,CAAC;MAClB;IACF;IACAL,UAAU,CAACM,GAAG,CACZ,IAAAV,gBAAW,EAACV,CAAC,EAAEwB,aAAa,CAACG,IAAI,CAAC1B,IAAI,CAAC,IAAI,EAAE,EAC7CuB,aACF,CAAC;EACH;EACA,MAAMI,OAAyB,GAAG,EAAE;EACpC,KAAK,MAAM3B,IAAI,IAAIsB,cAAc,EAAE;IACjC,MAAMC,aAAa,GAAGV,UAAU,CAACe,GAAG,CAAC5B,IAAI,CAAC;IAC1C,MAAM6B,SAAS,GAAGN,aAAa,aAAbA,aAAa,uBAAbA,aAAa,CAAEK,GAAG,CAAC,OAAO,CAAC;IAC7C,IAAIC,SAAS,IAAI,CAACC,KAAK,CAACC,OAAO,CAACF,SAAS,CAAC,IAAIA,SAAS,CAACH,IAAI,EAAE;MAC5DC,OAAO,CAACK,IAAI,CAACH,SAA6C,CAAC;IAC7D;EACF;EACA,OAAOF,OAAO,CAACM,MAAM,CAACZ,IAAI,CAACO,GAAG,CAAC,UAAU,CAAqB,CAAC;AACjE;AAEA,SAASM,YAAYA,CACnBC,IAAoB,EACpBC,SAAyB,EAChB;EACT,IAAIC,OAA8B,GAAGD,SAAS;EAC9C,OAAOC,OAAO,EAAE;IACd,IAAIA,OAAO,KAAKF,IAAI,IAAIE,OAAO,CAACX,IAAI,KAAKS,IAAI,CAACT,IAAI,EAAE;MAClD,OAAO,IAAI;IACb;IACAW,OAAO,GAAGA,OAAO,CAACC,UAAU;EAC9B;EACA,OAAO,KAAK;AACd;;AAEA;AACO,SAASC,yBAAyBA,CACvCxC,CAAqB,EACrBsB,IAA4C,EAC5C1B,SAA+B,EAC/B6C,gBAAyC,EAChC;EACT,MAAMb,OAAO,GAAGP,kBAAkB,CAACrB,CAAC,EAAEsB,IAAI,EAAE1B,SAAS,CAAC;EACtD,IAAI8C,OAAO,GAAG,IAAI;EAClB,MAAMC,cAAc,GAAGA,CAACC,aAA6B,EAAE3C,IAAY,KAAK;IACtE,MAAM4C,aAAa,GAAGD,aAAa,CAACE,KAAK,CAACC,UAAU,CAAC9C,IAAI,CAAC;IAC1D,IACE4C,aAAa,IACbjB,OAAO,CAACoB,IAAI,CAAEC,MAAM,IAAKd,YAAY,CAACc,MAAM,EAAEJ,aAAa,CAACvB,IAAI,CAAC,CAAC,EAClE;MACA;IACF;IACA,MAAM4B,kBAAkB,GAAGT,gBAAgB,CAACM,UAAU,CAAC9C,IAAI,CAAC;IAC5D,IAAI,CAAA4C,aAAa,aAAbA,aAAa,uBAAbA,aAAa,CAAEvC,UAAU,OAAK4C,kBAAkB,aAAlBA,kBAAkB,uBAAlBA,kBAAkB,CAAE5C,UAAU,GAAE;MAChEoC,OAAO,GAAG,KAAK;IACjB;EACF,CAAC;EAED,KAAK,MAAMO,MAAM,IAAIrB,OAAO,EAAE;IAC5BqB,MAAM,CAACE,QAAQ,CAAC;MACdC,oBAAoBA,CAACR,aAAa,EAAE;QAClCD,cAAc,CAACC,aAAa,EAAEA,aAAa,CAACjB,IAAI,CAAC1B,IAAI,CAAC;QACtD,IAAI,CAACyC,OAAO,EAAE;UACZE,aAAa,CAACS,IAAI,CAAC,CAAC;QACtB;MACF,CAAC;MACDC,iBAAiBA,CAACC,WAAW,EAAE;QAC7B,IAAItD,IAAI,GAAGsD,WAAW,CAAC5B,IAAI,CAAC1B,IAAI;QAChC,IAAIuD,QAAQ,GAAG,KAAK;QACpB,OAAOxD,CAAC,CAACE,qBAAqB,CAACD,IAAI,CAAC,EAAE;UACpCuD,QAAQ,GAAG,IAAI;UACfvD,IAAI,GAAGA,IAAI,CAACI,MAAM;QACpB;QACA,IACEL,CAAC,CAACI,eAAe,CAACH,IAAI,CAAC,KACtBuD,QAAQ,IAAI,CAAC,QAAQ,CAAC7C,IAAI,CAACV,IAAI,CAACA,IAAI,CAAC,CAAC,EACvC;UACA0C,cAAc,CAACY,WAAW,EAAEtD,IAAI,CAACA,IAAI,CAAC;QACxC;QACA,IAAI,CAACyC,OAAO,EAAE;UACZa,WAAW,CAACF,IAAI,CAAC,CAAC;QACpB;MACF,CAAC;MACDI,kBAAkBA,CAACC,UAAU,EAAE;QAC7B;QACAA,UAAU,CAACC,IAAI,CAAC,CAAC;MACnB;IACF,CAAC,CAAC;IACF,IAAI,CAACjB,OAAO,EAAE;MACZ,OAAO,KAAK;IACd;EACF;EACA,OAAO,IAAI;AACb;;AAEA;AACA,SAASkB,aAAaA,CACpB5D,CAAqB,EACrB2B,IAAsB,EACtBkC,KAA6B,EACX;EAClB,IAAI7D,CAAC,CAAC8D,YAAY,CAACnC,IAAI,CAAC,IAAI3B,CAAC,CAAC+D,aAAa,CAACpC,IAAI,CAAC,EAAE;IACjD,MAAMqC,UAAwC,GAAG,EAAE;IACnD,IAAIhE,CAAC,CAAC8D,YAAY,CAACnC,IAAI,CAAC,EAAE;MACxB,KAAK,MAAM,CAAC1B,IAAI,EAAEgB,SAAS,CAAC,IAAIJ,mBAAmB,CACjDb,CAAC,EACD2B,IAAI,CAACsC,cAAc,CAACnD,UACtB,CAAC,EAAE;QACDkD,UAAU,CAAC/B,IAAI,CACbjC,CAAC,CAACkE,cAAc,CACdlE,CAAC,CAACY,aAAa,CAACX,IAAI,CAAC,EACrBgB,SAAS,CAACR,KAAK,GACVmD,aAAa,CACZ5D,CAAC,EACDiB,SAAS,CAACR,KAAK,EACfoD,KACF,CAAC,GACD7D,CAAC,CAACmE,cAAc,CAAC,IAAI,CAC3B,CACF,CAAC;MACH;IACF;IACA,MAAMC,QAAQ,GAAGzC,IAAI,CAACyC,QAAQ,CAC3BC,MAAM,CACJC,KAAK,IACJ,EAAEtE,CAAC,CAACuE,SAAS,CAACD,KAAK,CAAC,IAAI,CAACA,KAAK,CAAC7D,KAAK,CAAC+D,IAAI,CAAC,CAAC,CAAC,IAC5C,EACExE,CAAC,CAACyE,wBAAwB,CAACH,KAAK,CAAC,IACjCtE,CAAC,CAAC0E,oBAAoB,CAACJ,KAAK,CAACK,UAAU,CAAC,CAE9C,CAAC,CACAC,GAAG,CAAEN,KAAK,IAAKV,aAAa,CAAC5D,CAAC,EAAEsE,KAAK,EAAET,KAAK,CAA2B,CAAC;IAC3E,OAAO7D,CAAC,CAAC6E,cAAc,CACrB7E,CAAC,CAACO,gBAAgB,CAACP,CAAC,CAAC8E,SAAS,CAACjB,KAAK,CAAC,EAAE7D,CAAC,CAACM,UAAU,CAAC,eAAe,CAAC,CAAC,EACrE,CACEN,CAAC,CAAC8D,YAAY,CAACnC,IAAI,CAAC,GAChB5B,WAAW,CAACC,CAAC,EAAE2B,IAAI,CAACsC,cAAc,CAAChE,IAAI,CAAC,GACxCD,CAAC,CAACO,gBAAgB,CAACP,CAAC,CAAC8E,SAAS,CAACjB,KAAK,CAAC,EAAE7D,CAAC,CAACM,UAAU,CAAC,UAAU,CAAC,CAAC,EACpE0D,UAAU,CAACe,MAAM,GAAG/E,CAAC,CAACgF,gBAAgB,CAAChB,UAAU,CAAC,GAAGhE,CAAC,CAACiF,WAAW,CAAC,CAAC,EACpE,GAAGb,QAAQ,CAEf,CAAC;EACH;EACA,IAAIpE,CAAC,CAACuE,SAAS,CAAC5C,IAAI,CAAC,EAAE;IACrB,OAAO3B,CAAC,CAACY,aAAa,CAACe,IAAI,CAAClB,KAAK,CAACyE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAACV,IAAI,CAAC,CAAC,CAAC;EAChE;EACA,IAAIxE,CAAC,CAACyE,wBAAwB,CAAC9C,IAAI,CAAC,EAAE;IACpC,OAAOiC,aAAa,CAAC5D,CAAC,EAAE2B,IAAI,CAACgD,UAAU,EAAEd,KAAK,CAAC;EACjD;EACA;EACA;EACA,MAAMsB,MAAM,GAAGnF,CAAC,CAAC8E,SAAS,CAACnD,IAAI,EAAE,KAAK,CAAC;EACvC,MAAMyD,MAAM,GAAGD,MAGd;EACD,KAAK,MAAME,GAAG,IAAIrF,CAAC,CAACsF,YAAY,CAAC3D,IAAI,CAAC4D,IAAI,CAAC,IAAI,EAAE,EAAE;IACjD,MAAM9E,KAAK,GAAG2E,MAAM,CAACC,GAAG,CAAC;IACzB,IAAItD,KAAK,CAACC,OAAO,CAACvB,KAAK,CAAC,EAAE;MACxB2E,MAAM,CAACC,GAAG,CAAC,GAAG5E,KAAK,CAACmE,GAAG,CAAEN,KAAK,IAC5BA,KAAK,GAAGV,aAAa,CAAC5D,CAAC,EAAEsE,KAAK,EAAET,KAAK,CAAC,GAAGS,KAC3C,CAAC;IACH,CAAC,MAAM,IAAI7D,KAAK,EAAE;MAChB2E,MAAM,CAACC,GAAG,CAAC,GAAGzB,aAAa,CAAC5D,CAAC,EAAES,KAAK,EAAEoD,KAAK,CAAC;IAC9C;EACF;EACA,OAAOsB,MAAM;AACf;;AAEA;AACO,SAASK,kBAAkBA,CAChCxF,CAAqB,EACrBsB,IAA4C,EAC5C1B,SAA+B,EAC/B6F,KAAkB,EAC0B;EAC5C,IAAI,CAAC7F,SAAS,CAAC8F,UAAU,EAAE;IACzB,OAAO,IAAI;EACb;EACA,MAAMnE,cAAc,GAAG5B,qBAAqB,CAACC,SAAS,CAAC;EACvD,MAAMkB,UAAU,GAAGD,mBAAmB,CACpCb,CAAC,EACDsB,IAAI,CAACK,IAAI,CAACsC,cAAc,CAACnD,UAC3B,CAAC;EACD,MAAM6E,UAAoC,GAAG,EAAE;EAC/C,KAAK,MAAM1F,IAAI,IAAIsB,cAAc,EAAE;IACjC,MAAMN,SAAS,GAAGH,UAAU,CAACe,GAAG,CAAC5B,IAAI,CAAC;IACtC,IAAIgB,SAAS,aAATA,SAAS,eAATA,SAAS,CAAER,KAAK,EAAE;MACpBkF,UAAU,CAAC1D,IAAI,CACb2B,aAAa,CACX5D,CAAC,EACDiB,SAAS,CAACR,KAAK,EACfgF,KAAK,CAACG,kBACR,CACF,CAAC;IACH;EACF;EACA,MAAMC,QAAQ,GAAG7F,CAAC,CAAC8F,WAAW,CAC5B9F,CAAC,CAAC+F,kBAAkB,CAAC,CAAC,EACtB/F,CAAC,CAACgG,kBAAkB,CAAC,CAAC,EACtB1E,IAAI,CAACK,IAAI,CAACyC,QACZ,CAAC;EACD,OAAOpE,CAAC,CAACiG,uBAAuB,CAC9B,EAAE,EACFjG,CAAC,CAAC6E,cAAc,CAAC7E,CAAC,CAAC8E,SAAS,CAACW,KAAK,CAACS,wBAAwB,CAAC,EAAE,CAC5DtC,aAAa,CACX5D,CAAC,EACD6F,QAAQ,EACRJ,KAAK,CAACG,kBACR,CAAC,EACD5F,CAAC,CAACmG,eAAe,CAACR,UAAU,CAAC,CAC9B,CACH,CAAC;AACH","ignoreList":[]}
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.handleJSXElement = handleJSXElement;
7
+ exports.insertRuntimeImports = insertRuntimeImports;
8
+ var _constants = require("../../constants");
9
+ var _jsx = require("../../utils/jsx");
10
+ var _components = require("./components");
11
+ var _metadata = require("./metadata");
12
+ var _memoization = require("./memoization");
13
+ var _tap = require("./tap");
14
+ /*
15
+ * Portions of this file are adapted from Datadog's dd-sdk-reactnative Babel
16
+ * interaction tracking implementation, licensed under Apache-2.0.
17
+ */
18
+
19
+ function handleJSXElement(t, path, state, options) {
20
+ const componentName = (0, _jsx.getNodeName)(t, path.node.openingElement.name);
21
+ const component = componentName ? state._ftTrackedComponents[componentName] : undefined;
22
+ if (!componentName || !component || (0, _components.isInsideConfiguredComponent)(t, path, state._ftCustomNames)) {
23
+ return;
24
+ }
25
+ (0, _components.addRequiredTextInputHandler)(t, path, component, componentName);
26
+ let metadata;
27
+ for (const attributePath of (0, _jsx.getDirectAttributePaths)(path)) {
28
+ var _attributePath$node$e;
29
+ if (!attributePath.isJSXAttribute() || (_attributePath$node$e = attributePath.node.extra) !== null && _attributePath$node$e !== void 0 && _attributePath$node$e.__ftWrappedAction) {
30
+ continue;
31
+ }
32
+ const attributeName = (0, _jsx.getNodeName)(t, attributePath.node.name);
33
+ const handler = component.handlers.find(item => item.event === attributeName);
34
+ if (!handler) {
35
+ continue;
36
+ }
37
+ const value = attributePath.node.value;
38
+ if (!t.isJSXExpressionContainer(value)) {
39
+ continue;
40
+ }
41
+ const expression = value.expression;
42
+ if (!t.isArrowFunctionExpression(expression) && !t.isFunctionExpression(expression) && !t.isIdentifier(expression) && !t.isMemberExpression(expression) && !t.isConditionalExpression(expression)) {
43
+ continue;
44
+ }
45
+ metadata || (metadata = (0, _metadata.buildMetadata)(t, path, componentName, component, options, state));
46
+ if (!(0, _memoization.wrapMemoizedHandler)(t, attributePath, expression, metadata, state, handler.mode, handler.actionType)) {
47
+ const wrappedHandler = t.isConditionalExpression(expression) ? (0, _tap.buildGuardedHandlerWrapper)(t, expression, metadata, state, attributePath.scope, handler.mode, handler.actionType) : (0, _tap.buildHandlerWrapper)(t, expression, metadata, state, attributePath.scope, handler.mode, handler.actionType);
48
+ attributePath.node.value = t.jsxExpressionContainer(wrappedHandler);
49
+ state._ftHasWrappedAction = true;
50
+ }
51
+ attributePath.node.extra = {
52
+ ...attributePath.node.extra,
53
+ __ftWrappedAction: true
54
+ };
55
+ }
56
+ }
57
+ function insertRuntimeImports(t, programPath, state) {
58
+ if (!state._ftHasWrappedAction) {
59
+ return;
60
+ }
61
+ const imports = [t.importSpecifier(t.cloneNode(state._ftTrackingIdentifier), t.identifier('FTBabelInteractionTracking'))];
62
+ if (state._ftNeedsContentRuntime) {
63
+ imports.push(t.importSpecifier(t.cloneNode(state._ftExtractTextIdentifier), t.identifier('__ftExtractText')));
64
+ programPath.unshiftContainer('body', t.importDeclaration([t.importNamespaceSpecifier(t.cloneNode(state._ftReactIdentifier))], t.stringLiteral('react')));
65
+ }
66
+ programPath.unshiftContainer('body', t.importDeclaration(imports, t.stringLiteral(_constants.RUNTIME_PACKAGE)));
67
+ }
68
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_constants","require","_jsx","_components","_metadata","_memoization","_tap","handleJSXElement","t","path","state","options","componentName","getNodeName","node","openingElement","name","component","_ftTrackedComponents","undefined","isInsideConfiguredComponent","_ftCustomNames","addRequiredTextInputHandler","metadata","attributePath","getDirectAttributePaths","_attributePath$node$e","isJSXAttribute","extra","__ftWrappedAction","attributeName","handler","handlers","find","item","event","value","isJSXExpressionContainer","expression","isArrowFunctionExpression","isFunctionExpression","isIdentifier","isMemberExpression","isConditionalExpression","buildMetadata","wrapMemoizedHandler","mode","actionType","wrappedHandler","buildGuardedHandlerWrapper","scope","buildHandlerWrapper","jsxExpressionContainer","_ftHasWrappedAction","insertRuntimeImports","programPath","imports","importSpecifier","cloneNode","_ftTrackingIdentifier","identifier","_ftNeedsContentRuntime","push","_ftExtractTextIdentifier","unshiftContainer","importDeclaration","importNamespaceSpecifier","_ftReactIdentifier","stringLiteral","RUNTIME_PACKAGE"],"sources":["index.ts"],"sourcesContent":["/*\n * Portions of this file are adapted from Datadog's dd-sdk-reactnative Babel\n * interaction tracking implementation, licensed under Apache-2.0.\n */\n\nimport type * as Babel from '@babel/core';\nimport { RUNTIME_PACKAGE } from '../../constants';\nimport type {\n ActionMetadata,\n NormalizedPluginOptions,\n PluginState,\n} from '../../types';\nimport { getDirectAttributePaths, getNodeName } from '../../utils/jsx';\nimport {\n addRequiredTextInputHandler,\n isInsideConfiguredComponent,\n} from './components';\nimport { buildMetadata } from './metadata';\nimport { wrapMemoizedHandler } from './memoization';\nimport { buildGuardedHandlerWrapper, buildHandlerWrapper } from './tap';\n\nexport function handleJSXElement(\n t: typeof Babel.types,\n path: Babel.NodePath<Babel.types.JSXElement>,\n state: PluginState,\n options: NormalizedPluginOptions\n): void {\n const componentName = getNodeName(t, path.node.openingElement.name);\n const component = componentName\n ? state._ftTrackedComponents[componentName]\n : undefined;\n if (\n !componentName ||\n !component ||\n isInsideConfiguredComponent(t, path, state._ftCustomNames)\n ) {\n return;\n }\n\n addRequiredTextInputHandler(t, path, component, componentName);\n let metadata: ActionMetadata | undefined;\n\n for (const attributePath of getDirectAttributePaths(path)) {\n if (\n !attributePath.isJSXAttribute() ||\n attributePath.node.extra?.__ftWrappedAction\n ) {\n continue;\n }\n const attributeName = getNodeName(t, attributePath.node.name);\n const handler = component.handlers.find(\n (item) => item.event === attributeName\n );\n if (!handler) {\n continue;\n }\n const value = attributePath.node.value;\n if (!t.isJSXExpressionContainer(value)) {\n continue;\n }\n const expression = value.expression;\n if (\n !t.isArrowFunctionExpression(expression) &&\n !t.isFunctionExpression(expression) &&\n !t.isIdentifier(expression) &&\n !t.isMemberExpression(expression) &&\n !t.isConditionalExpression(expression)\n ) {\n continue;\n }\n\n metadata ||= buildMetadata(\n t,\n path,\n componentName,\n component,\n options,\n state\n );\n\n if (\n !wrapMemoizedHandler(\n t,\n attributePath,\n expression,\n metadata,\n state,\n handler.mode,\n handler.actionType\n )\n ) {\n const wrappedHandler = t.isConditionalExpression(expression)\n ? buildGuardedHandlerWrapper(\n t,\n expression,\n metadata,\n state,\n attributePath.scope,\n handler.mode,\n handler.actionType\n )\n : buildHandlerWrapper(\n t,\n expression,\n metadata,\n state,\n attributePath.scope,\n handler.mode,\n handler.actionType\n );\n attributePath.node.value = t.jsxExpressionContainer(wrappedHandler);\n state._ftHasWrappedAction = true;\n }\n attributePath.node.extra = {\n ...attributePath.node.extra,\n __ftWrappedAction: true,\n };\n }\n}\n\nexport function insertRuntimeImports(\n t: typeof Babel.types,\n programPath: Babel.NodePath<Babel.types.Program>,\n state: PluginState\n): void {\n if (!state._ftHasWrappedAction) {\n return;\n }\n const imports = [\n t.importSpecifier(\n t.cloneNode(state._ftTrackingIdentifier),\n t.identifier('FTBabelInteractionTracking')\n ),\n ];\n if (state._ftNeedsContentRuntime) {\n imports.push(\n t.importSpecifier(\n t.cloneNode(state._ftExtractTextIdentifier),\n t.identifier('__ftExtractText')\n )\n );\n programPath.unshiftContainer(\n 'body',\n t.importDeclaration(\n [t.importNamespaceSpecifier(t.cloneNode(state._ftReactIdentifier))],\n t.stringLiteral('react')\n )\n );\n }\n programPath.unshiftContainer(\n 'body',\n t.importDeclaration(imports, t.stringLiteral(RUNTIME_PACKAGE))\n );\n}\n"],"mappings":"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","ignoreList":[]}