@flisk/analyze-tracking 0.8.6 → 0.8.7

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 CHANGED
@@ -101,18 +101,19 @@ See [schema.json](schema.json) for a JSON Schema of the output.
101
101
 
102
102
  | Library | JavaScript/TypeScript | Python | Ruby | Go |
103
103
  |---------|:---------------------:|:------:|:----:|:--:|
104
- | Google Analytics | ✅ | ❌ | ❌ | ❌ |
105
- | Segment | ✅ | | | |
106
- | Mixpanel | ✅ | ✅ | ✅ | ✅ |
107
- | Amplitude | ✅ | ✅ | | ✅ |
108
- | Rudderstack | ✅ | ✅ | ✳️ | ✳️ |
109
- | mParticle | ✅ | | | |
110
- | PostHog | ✅ | | | |
111
- | Pendo | ✅ | | | |
112
- | Heap | ✅ | ❌ | ❌ | ❌ |
113
- | Snowplow | ✅ | | | |
114
- | Datadog RUM | ✅ | | | |
115
- | Custom Function | ✅ | | | |
104
+ | Google Analytics | ✅ | ❌ | ❌ | ❌ |
105
+ | Google Tag Manager | ✅ | | | |
106
+ | Segment | ✅ | ✅ | ✅ | ✅ |
107
+ | Mixpanel | ✅ | ✅ | | ✅ |
108
+ | Amplitude | ✅ | ✅ | | |
109
+ | Rudderstack | ✅ | | ✳️ | ✳️ |
110
+ | mParticle | ✅ | | | |
111
+ | PostHog | ✅ | | | |
112
+ | Pendo | ✅ | ❌ | ❌ | ❌ |
113
+ | Heap | ✅ | | | |
114
+ | Snowplow | ✅ | | | |
115
+ | Datadog RUM | ✅ | | | |
116
+ | Custom Function | ✅ | ✅ | ✅ | ✅ |
116
117
 
117
118
  ✳️ Rudderstack's SDKs often use the same format as Segment, so Rudderstack events may be detected as Segment events.
118
119
 
@@ -130,6 +131,24 @@ See [schema.json](schema.json) for a JSON Schema of the output.
130
131
  ```
131
132
  </details>
132
133
 
134
+ <details>
135
+ <summary>Google Tag Manager</summary>
136
+
137
+ **JavaScript/TypeScript**
138
+ ```js
139
+ dataLayer.push({
140
+ event: '<event_name>',
141
+ '<property_name>': '<property_value>'
142
+ });
143
+
144
+ // Or via window
145
+ window.dataLayer.push({
146
+ event: '<event_name>',
147
+ '<property_name>': '<property_value>'
148
+ });
149
+ ```
150
+ </details>
151
+
133
152
  <details>
134
153
  <summary>Segment</summary>
135
154
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flisk/analyze-tracking",
3
- "version": "0.8.6",
3
+ "version": "0.8.7",
4
4
  "description": "Analyzes tracking code in a project and generates data schemas",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/schema.json CHANGED
@@ -68,6 +68,7 @@
68
68
  "heap",
69
69
  "snowplow",
70
70
  "datadog",
71
+ "gtm",
71
72
  "custom",
72
73
  "unknown"
73
74
  ],
@@ -81,6 +81,12 @@ const ANALYTICS_PROVIDERS = {
81
81
  objectNames: ['datadogRum', 'DD_RUM'],
82
82
  methodName: 'addAction',
83
83
  type: 'member'
84
+ },
85
+ GOOGLE_TAG_MANAGER: {
86
+ name: 'gtm',
87
+ objectNames: ['dataLayer'],
88
+ methodName: 'push',
89
+ type: 'member'
84
90
  }
85
91
  };
86
92
 
@@ -20,6 +20,7 @@ const EXTRACTION_STRATEGIES = {
20
20
  googleanalytics: extractGoogleAnalyticsEvent,
21
21
  snowplow: extractSnowplowEvent,
22
22
  mparticle: extractMparticleEvent,
23
+ gtm: extractGTMEvent,
23
24
  custom: extractCustomEvent,
24
25
  default: extractDefaultEvent
25
26
  };
@@ -105,6 +106,43 @@ function extractMparticleEvent(node, constantMap) {
105
106
  return { eventName, propertiesNode };
106
107
  }
107
108
 
109
+ /**
110
+ * Extracts Google Tag Manager event data
111
+ * @param {Object} node - CallExpression node
112
+ * @param {Object} constantMap - Collected constant map
113
+ * @returns {EventData}
114
+ */
115
+ function extractGTMEvent(node, constantMap) {
116
+ if (!node.arguments || node.arguments.length === 0) {
117
+ return { eventName: null, propertiesNode: null };
118
+ }
119
+
120
+ // dataLayer.push({ event: 'event_name', property1: 'value1', property2: 'value2' })
121
+ const firstArg = node.arguments[0];
122
+
123
+ if (firstArg.type !== NODE_TYPES.OBJECT_EXPRESSION) {
124
+ return { eventName: null, propertiesNode: null };
125
+ }
126
+
127
+ // Find the 'event' property
128
+ const eventProperty = findPropertyByKey(firstArg, 'event');
129
+ if (!eventProperty) {
130
+ return { eventName: null, propertiesNode: null };
131
+ }
132
+
133
+ const eventName = getStringValue(eventProperty.value, constantMap);
134
+
135
+ // Create a modified properties node without the 'event' property
136
+ const modifiedPropertiesNode = {
137
+ ...firstArg,
138
+ properties: firstArg.properties.filter(prop =>
139
+ prop.key && (prop.key.name !== 'event' && prop.key.value !== 'event')
140
+ )
141
+ };
142
+
143
+ return { eventName, propertiesNode: modifiedPropertiesNode };
144
+ }
145
+
108
146
  /**
109
147
  * Default event extraction for standard providers
110
148
  * @param {Object} node - CallExpression node
@@ -81,6 +81,12 @@ const ANALYTICS_PROVIDERS = {
81
81
  objectNames: ['datadogRum', 'DD_RUM'],
82
82
  methodName: 'addAction',
83
83
  type: 'member'
84
+ },
85
+ GOOGLE_TAG_MANAGER: {
86
+ name: 'gtm',
87
+ objectNames: ['dataLayer'],
88
+ methodName: 'push',
89
+ type: 'member'
84
90
  }
85
91
  };
86
92
 
@@ -21,6 +21,7 @@ const EXTRACTION_STRATEGIES = {
21
21
  googleanalytics: extractGoogleAnalyticsEvent,
22
22
  snowplow: extractSnowplowEvent,
23
23
  mparticle: extractMparticleEvent,
24
+ gtm: extractGTMEvent,
24
25
  custom: extractCustomEvent,
25
26
  default: extractDefaultEvent
26
27
  };
@@ -126,6 +127,60 @@ function extractMparticleEvent(node, checker, sourceFile) {
126
127
  return { eventName, propertiesNode };
127
128
  }
128
129
 
130
+ /**
131
+ * Extracts Google Tag Manager event data
132
+ * @param {Object} node - CallExpression node
133
+ * @param {Object} checker - TypeScript type checker
134
+ * @param {Object} sourceFile - TypeScript source file
135
+ * @returns {EventData}
136
+ */
137
+ function extractGTMEvent(node, checker, sourceFile) {
138
+ if (!node.arguments || node.arguments.length === 0) {
139
+ return { eventName: null, propertiesNode: null };
140
+ }
141
+
142
+ // dataLayer.push({ event: 'event_name', property1: 'value1', property2: 'value2' })
143
+ const firstArg = node.arguments[0];
144
+
145
+ if (!ts.isObjectLiteralExpression(firstArg)) {
146
+ return { eventName: null, propertiesNode: null };
147
+ }
148
+
149
+ // Find the 'event' property
150
+ const eventProperty = findPropertyByKey(firstArg, 'event');
151
+ if (!eventProperty) {
152
+ return { eventName: null, propertiesNode: null };
153
+ }
154
+
155
+ const eventName = getStringValue(eventProperty.initializer, checker, sourceFile);
156
+
157
+ // Create a modified properties node without the 'event' property
158
+ const modifiedProperties = firstArg.properties.filter(prop => {
159
+ if (ts.isPropertyAssignment(prop) && prop.name) {
160
+ if (ts.isIdentifier(prop.name)) {
161
+ return prop.name.escapedText !== 'event';
162
+ }
163
+ if (ts.isStringLiteral(prop.name)) {
164
+ return prop.name.text !== 'event';
165
+ }
166
+ }
167
+ return true;
168
+ });
169
+
170
+ // Create a synthetic object literal with the filtered properties
171
+ const modifiedPropertiesNode = ts.factory.createObjectLiteralExpression(modifiedProperties);
172
+
173
+ // Copy source positions for proper analysis
174
+ if (firstArg.pos !== undefined) {
175
+ modifiedPropertiesNode.pos = firstArg.pos;
176
+ }
177
+ if (firstArg.end !== undefined) {
178
+ modifiedPropertiesNode.end = firstArg.end;
179
+ }
180
+
181
+ return { eventName, propertiesNode: modifiedPropertiesNode };
182
+ }
183
+
129
184
  /**
130
185
  * Custom extraction
131
186
  * @param {Object} node - CallExpression node