@sentry/wizard 3.16.4 → 3.17.0

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 (52) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/package.json +2 -2
  3. package/dist/src/apple/cocoapod.js +2 -0
  4. package/dist/src/apple/cocoapod.js.map +1 -1
  5. package/dist/src/react-native/metro.d.ts +13 -0
  6. package/dist/src/react-native/metro.js +398 -0
  7. package/dist/src/react-native/metro.js.map +1 -0
  8. package/dist/src/react-native/react-native-wizard.d.ts +2 -0
  9. package/dist/src/react-native/react-native-wizard.js +145 -42
  10. package/dist/src/react-native/react-native-wizard.js.map +1 -1
  11. package/dist/src/react-native/uninstall.js +4 -0
  12. package/dist/src/react-native/uninstall.js.map +1 -1
  13. package/dist/src/react-native/xcode.d.ts +7 -3
  14. package/dist/src/react-native/xcode.js +43 -11
  15. package/dist/src/react-native/xcode.js.map +1 -1
  16. package/dist/src/remix/remix-wizard.js +80 -37
  17. package/dist/src/remix/remix-wizard.js.map +1 -1
  18. package/dist/src/remix/sdk-setup.d.ts +1 -0
  19. package/dist/src/remix/sdk-setup.js +21 -1
  20. package/dist/src/remix/sdk-setup.js.map +1 -1
  21. package/dist/src/sourcemaps/sourcemaps-wizard.js +2 -6
  22. package/dist/src/sourcemaps/sourcemaps-wizard.js.map +1 -1
  23. package/dist/src/utils/ast-utils.d.ts +14 -0
  24. package/dist/src/utils/ast-utils.js +49 -1
  25. package/dist/src/utils/ast-utils.js.map +1 -1
  26. package/dist/src/utils/clack-utils.js +0 -1
  27. package/dist/src/utils/clack-utils.js.map +1 -1
  28. package/dist/src/utils/types.d.ts +8 -4
  29. package/dist/src/utils/types.js.map +1 -1
  30. package/dist/src/utils/url.d.ts +10 -0
  31. package/dist/src/utils/url.js +19 -0
  32. package/dist/src/utils/url.js.map +1 -0
  33. package/dist/test/react-native/metro.test.d.ts +1 -0
  34. package/dist/test/react-native/metro.test.js +125 -0
  35. package/dist/test/react-native/metro.test.js.map +1 -0
  36. package/dist/test/react-native/xcode.test.js +40 -2
  37. package/dist/test/react-native/xcode.test.js.map +1 -1
  38. package/package.json +2 -2
  39. package/src/apple/cocoapod.ts +2 -0
  40. package/src/react-native/metro.ts +409 -0
  41. package/src/react-native/react-native-wizard.ts +115 -12
  42. package/src/react-native/uninstall.ts +3 -0
  43. package/src/react-native/xcode.ts +70 -12
  44. package/src/remix/remix-wizard.ts +51 -15
  45. package/src/remix/sdk-setup.ts +31 -0
  46. package/src/sourcemaps/sourcemaps-wizard.ts +2 -7
  47. package/src/utils/ast-utils.ts +52 -0
  48. package/src/utils/clack-utils.ts +0 -1
  49. package/src/utils/types.ts +8 -5
  50. package/src/utils/url.ts +23 -0
  51. package/test/react-native/metro.test.ts +283 -0
  52. package/test/react-native/xcode.test.ts +76 -3
@@ -0,0 +1,283 @@
1
+ // @ts-ignore - magicast is ESM and TS complains about that. It works though
2
+ import { generateCode, type ProxifiedModule, parseModule } from 'magicast';
3
+
4
+ import * as recast from 'recast';
5
+ import x = recast.types;
6
+ import t = x.namedTypes;
7
+
8
+ import {
9
+ addSentrySerializerRequireToMetroConfig,
10
+ addSentrySerializerToMetroConfig,
11
+ getMetroConfigObject,
12
+ removeSentryRequire,
13
+ removeSentrySerializerFromMetroConfig,
14
+ } from '../../src/react-native/metro';
15
+
16
+ describe('patch metro config - sentry serializer', () => {
17
+ describe('addSentrySerializerToMetroConfig', () => {
18
+ it('add to empty config', () => {
19
+ const mod = parseModule(`module.exports = {
20
+ other: 'config'
21
+ }`);
22
+ const configObject = getModuleExportsObject(mod);
23
+ const result = addSentrySerializerToMetroConfig(configObject);
24
+ expect(result).toBe(true);
25
+ expect(generateCode(mod.$ast).code).toBe(`module.exports = {
26
+ other: 'config',
27
+
28
+ serializer: {
29
+ customSerializer: createSentryMetroSerializer()
30
+ }
31
+ }`);
32
+ });
33
+
34
+ it('add to existing serializer config', () => {
35
+ const mod = parseModule(`module.exports = {
36
+ other: 'config',
37
+ serializer: {
38
+ other: 'config'
39
+ }
40
+ }`);
41
+ const configObject = getModuleExportsObject(mod);
42
+ const result = addSentrySerializerToMetroConfig(configObject);
43
+ expect(result).toBe(true);
44
+ expect(generateCode(mod.$ast).code).toBe(`module.exports = {
45
+ other: 'config',
46
+ serializer: {
47
+ other: 'config',
48
+ customSerializer: createSentryMetroSerializer()
49
+ }
50
+ }`);
51
+ });
52
+
53
+ it('not add to existing customSerializer config', () => {
54
+ const mod = parseModule(`module.exports = {
55
+ other: 'config',
56
+ serializer: {
57
+ other: 'config',
58
+ customSerializer: 'existing-serializer'
59
+ }
60
+ }`);
61
+ const configObject = getModuleExportsObject(mod);
62
+ const result = addSentrySerializerToMetroConfig(configObject);
63
+ expect(result).toBe(false);
64
+ expect(generateCode(mod.$ast).code).toBe(`module.exports = {
65
+ other: 'config',
66
+ serializer: {
67
+ other: 'config',
68
+ customSerializer: 'existing-serializer'
69
+ }
70
+ }`);
71
+ });
72
+ });
73
+
74
+ describe('addSentrySerializerImportToMetroConfig', () => {
75
+ it('add import', () => {
76
+ const mod =
77
+ parseModule(`const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
78
+
79
+ module.exports = {
80
+ other: 'config'
81
+ }`);
82
+ const result = addSentrySerializerRequireToMetroConfig(
83
+ mod.$ast as t.Program,
84
+ );
85
+ expect(result).toBe(true);
86
+ expect(generateCode(mod.$ast).code)
87
+ .toBe(`const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
88
+
89
+ const {
90
+ createSentryMetroSerializer
91
+ } = require("@sentry/react-native/dist/js/tools/sentryMetroSerializer");
92
+
93
+ module.exports = {
94
+ other: 'config'
95
+ }`);
96
+ });
97
+ });
98
+
99
+ describe('getMetroConfigObject', () => {
100
+ it('get config object from variable called config', () => {
101
+ const mod = parseModule(`var config = { some: 'config' };`);
102
+ const configObject = getMetroConfigObject(mod.$ast as t.Program);
103
+ expect(
104
+ ((configObject?.properties[0] as t.ObjectProperty).key as t.Identifier)
105
+ .name,
106
+ ).toBe('some');
107
+ expect(
108
+ (
109
+ (configObject?.properties[0] as t.ObjectProperty)
110
+ .value as t.StringLiteral
111
+ ).value,
112
+ ).toBe('config');
113
+ });
114
+
115
+ it('get config object from const called config', () => {
116
+ const mod = parseModule(`const config = { some: 'config' };`);
117
+ const configObject = getMetroConfigObject(mod.$ast as t.Program);
118
+ expect(
119
+ ((configObject?.properties[0] as t.ObjectProperty).key as t.Identifier)
120
+ .name,
121
+ ).toBe('some');
122
+ expect(
123
+ (
124
+ (configObject?.properties[0] as t.ObjectProperty)
125
+ .value as t.StringLiteral
126
+ ).value,
127
+ ).toBe('config');
128
+ });
129
+
130
+ it('get config oject from let called config', () => {
131
+ const mod = parseModule(`let config = { some: 'config' };`);
132
+ const configObject = getMetroConfigObject(mod.$ast as t.Program);
133
+ expect(
134
+ ((configObject?.properties[0] as t.ObjectProperty).key as t.Identifier)
135
+ .name,
136
+ ).toBe('some');
137
+ expect(
138
+ (
139
+ (configObject?.properties[0] as t.ObjectProperty)
140
+ .value as t.StringLiteral
141
+ ).value,
142
+ ).toBe('config');
143
+ });
144
+
145
+ it('get config object from module.exports', () => {
146
+ const mod = parseModule(`module.exports = { some: 'config' };`);
147
+ const configObject = getMetroConfigObject(mod.$ast as t.Program);
148
+ expect(
149
+ ((configObject?.properties[0] as t.ObjectProperty).key as t.Identifier)
150
+ .name,
151
+ ).toBe('some');
152
+ expect(
153
+ (
154
+ (configObject?.properties[0] as t.ObjectProperty)
155
+ .value as t.StringLiteral
156
+ ).value,
157
+ ).toBe('config');
158
+ });
159
+ });
160
+
161
+ describe('remove @sentry require', () => {
162
+ it('nothing to remove', () => {
163
+ const mod = parseModule(`let config = { some: 'config' };`);
164
+ const result = removeSentryRequire(mod.$ast as t.Program);
165
+ expect(result).toBe(false);
166
+ expect(generateCode(mod.$ast).code).toBe(
167
+ `let config = { some: 'config' };`,
168
+ );
169
+ });
170
+
171
+ it('remove metro serializer import', () => {
172
+ const mod = parseModule(`const {
173
+ createSentryMetroSerializer,
174
+ } = require('@sentry/react-native/dist/js/tools/sentryMetroSerializer');
175
+ let config = { some: 'config' };`);
176
+ const result = removeSentryRequire(mod.$ast as t.Program);
177
+ expect(result).toBe(true);
178
+ expect(generateCode(mod.$ast).code).toBe(
179
+ `let config = { some: 'config' };`,
180
+ );
181
+ });
182
+
183
+ it('remove all sentry imports', () => {
184
+ const mod = parseModule(`const {
185
+ createSentryMetroSerializer,
186
+ } = require('@sentry/react-native/dist/js/tools/sentryMetroSerializer');
187
+ var Sentry = require('@sentry/react-native');
188
+ let SentryIntegrations = require('@sentry/integrations');
189
+
190
+ let config = { some: 'config' };`);
191
+ const result = removeSentryRequire(mod.$ast as t.Program);
192
+ expect(result).toBe(true);
193
+ expect(generateCode(mod.$ast).code).toBe(
194
+ `let config = { some: 'config' };`,
195
+ );
196
+ });
197
+ });
198
+
199
+ describe('remove sentryMetroSerializer', () => {
200
+ it('no custom serializer to remove', () => {
201
+ const mod = parseModule(`let config = { some: 'config' };`);
202
+ const result = removeSentrySerializerFromMetroConfig(
203
+ mod.$ast as t.Program,
204
+ );
205
+ expect(result).toBe(false);
206
+ expect(generateCode(mod.$ast).code).toBe(
207
+ `let config = { some: 'config' };`,
208
+ );
209
+ });
210
+
211
+ it('no Sentry custom serializer to remove', () => {
212
+ const mod = parseModule(`let config = {
213
+ serializer: {
214
+ customSerializer: 'existing-serializer',
215
+ other: 'config',
216
+ },
217
+ other: 'config',
218
+ };`);
219
+ const result = removeSentrySerializerFromMetroConfig(
220
+ mod.$ast as t.Program,
221
+ );
222
+ expect(result).toBe(false);
223
+ expect(generateCode(mod.$ast).code).toBe(`let config = {
224
+ serializer: {
225
+ customSerializer: 'existing-serializer',
226
+ other: 'config',
227
+ },
228
+ other: 'config',
229
+ };`);
230
+ });
231
+
232
+ it('Sentry serializer to remove', () => {
233
+ const mod = parseModule(`let config = {
234
+ serializer: {
235
+ customSerializer: createSentryMetroSerializer(),
236
+ other: 'config',
237
+ },
238
+ other: 'config',
239
+ };`);
240
+ const result = removeSentrySerializerFromMetroConfig(
241
+ mod.$ast as t.Program,
242
+ );
243
+ expect(result).toBe(true);
244
+ expect(generateCode(mod.$ast).code).toBe(`let config = {
245
+ serializer: {
246
+ other: 'config'
247
+ },
248
+ other: 'config',
249
+ };`);
250
+ });
251
+
252
+ it('Sentry serializer to remove with wrapped serializer', () => {
253
+ const mod = parseModule(`let config = {
254
+ serializer: {
255
+ customSerializer: createSentryMetroSerializer(wrappedSerializer()),
256
+ other: 'config',
257
+ },
258
+ other: 'config',
259
+ };`);
260
+ const result = removeSentrySerializerFromMetroConfig(
261
+ mod.$ast as t.Program,
262
+ );
263
+ expect(result).toBe(true);
264
+ expect(generateCode(mod.$ast).code).toBe(`let config = {
265
+ serializer: {
266
+ customSerializer: wrappedSerializer(),
267
+ other: 'config',
268
+ },
269
+ other: 'config',
270
+ };`);
271
+ });
272
+ });
273
+ });
274
+
275
+ function getModuleExportsObject(
276
+ mod: ProxifiedModule,
277
+ index = 0,
278
+ ): t.ObjectExpression {
279
+ return (
280
+ ((mod.$ast as t.Program).body[index] as t.ExpressionStatement)
281
+ .expression as t.AssignmentExpression
282
+ ).right as t.ObjectExpression;
283
+ }
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable no-useless-escape */
2
2
  import {
3
- addSentryToBundleShellScript,
3
+ addSentryWithBundledScriptsToBundleShellScript,
4
+ addSentryWithCliToBundleShellScript,
4
5
  doesBundlePhaseIncludeSentry,
5
6
  findBundlePhase,
6
7
  findDebugFilesUploadPhase,
@@ -8,7 +9,7 @@ import {
8
9
  } from '../../src/react-native/xcode';
9
10
 
10
11
  describe('react-native xcode', () => {
11
- describe('addSentryToBundleShellScript', () => {
12
+ describe('addSentryWithCliToBundleShellScript', () => {
12
13
  it('adds sentry cli to rn bundle build phase', () => {
13
14
  const input = `set -e
14
15
 
@@ -30,7 +31,31 @@ REACT_NATIVE_XCODE="../node_modules/react-native/scripts/react-native-xcode.sh"
30
31
  /bin/sh -c "$WITH_ENVIRONMENT ../node_modules/@sentry/react-native/scripts/collect-modules.sh"
31
32
  `;
32
33
 
33
- expect(addSentryToBundleShellScript(input)).toBe(expectedOutput);
34
+ expect(addSentryWithCliToBundleShellScript(input)).toBe(expectedOutput);
35
+ });
36
+ });
37
+
38
+ describe('addSentryBundledScriptsToBundleShellScript', () => {
39
+ it('adds sentry cli to rn bundle build phase', () => {
40
+ const input = `set -e
41
+
42
+ WITH_ENVIRONMENT="../node_modules/react-native/scripts/xcode/with-environment.sh"
43
+ REACT_NATIVE_XCODE="../node_modules/react-native/scripts/react-native-xcode.sh"
44
+
45
+ /bin/sh -c "$WITH_ENVIRONMENT $REACT_NATIVE_XCODE"`;
46
+ // actual shell script looks like this:
47
+ // /bin/sh -c "$WITH_ENVIRONMENT \"$REACT_NATIVE_XCODE\""
48
+ // but during parsing xcode library removes the quotes
49
+ const expectedOutput = `set -e
50
+
51
+ WITH_ENVIRONMENT="../node_modules/react-native/scripts/xcode/with-environment.sh"
52
+ REACT_NATIVE_XCODE="../node_modules/react-native/scripts/react-native-xcode.sh"
53
+
54
+ /bin/sh -c "$WITH_ENVIRONMENT \\"/bin/sh ../node_modules/@sentry/react-native/scripts/sentry-xcode.sh $REACT_NATIVE_XCODE\\""`;
55
+
56
+ expect(addSentryWithBundledScriptsToBundleShellScript(input)).toBe(
57
+ expectedOutput,
58
+ );
34
59
  });
35
60
  });
36
61
 
@@ -59,6 +84,23 @@ REACT_NATIVE_XCODE="../node_modules/react-native/scripts/react-native-xcode.sh"
59
84
 
60
85
  expect(removeSentryFromBundleShellScript(input)).toBe(expectedOutput);
61
86
  });
87
+
88
+ it('removes sentry bundled scripts from rn bundle build phase', () => {
89
+ const input = `set -e
90
+
91
+ WITH_ENVIRONMENT="../node_modules/react-native/scripts/xcode/with-environment.sh"
92
+ REACT_NATIVE_XCODE="../node_modules/react-native/scripts/react-native-xcode.sh"
93
+
94
+ /bin/sh -c "$WITH_ENVIRONMENT \"/bin/sh ../node_modules/@sentry/react-native/scripts/sentry-xcode.sh $REACT_NATIVE_XCODE\""`;
95
+ const expectedOutput = `set -e
96
+
97
+ WITH_ENVIRONMENT="../node_modules/react-native/scripts/xcode/with-environment.sh"
98
+ REACT_NATIVE_XCODE="../node_modules/react-native/scripts/react-native-xcode.sh"
99
+
100
+ /bin/sh -c "$WITH_ENVIRONMENT \"$REACT_NATIVE_XCODE\""`;
101
+
102
+ expect(removeSentryFromBundleShellScript(input)).toBe(expectedOutput);
103
+ });
62
104
  });
63
105
 
64
106
  describe('findBundlePhase', () => {
@@ -138,6 +180,19 @@ SENTRY_CLI="sentry-cli react-native xcode"
138
180
  expect(doesBundlePhaseIncludeSentry(input)).toBeTruthy();
139
181
  });
140
182
 
183
+ it('returns true for script containing sentry bundled script', () => {
184
+ const input = {
185
+ shellScript: `set -e
186
+ WITH_ENVIRONMENT="../node_modules/react-native/scripts/xcode/with-environment.sh"
187
+ REACT_NATIVE_XCODE="../node_modules/react-native/scripts/react-native-xcode.sh"
188
+ SENTRY_CLI="sentry-cli react-native xcode"
189
+
190
+ /bin/sh -c "$WITH_ENVIRONMENT \\"/bin/sh ../node_modules/@sentry/react-native/scripts/sentry-xcode.sh $REACT_NATIVE_XCODE"\\"
191
+ `,
192
+ };
193
+ expect(doesBundlePhaseIncludeSentry(input)).toBeTruthy();
194
+ });
195
+
141
196
  it('returns false', () => {
142
197
  const input = {
143
198
  // note sentry-cli can be part of the script but doesn't call react native xcode script
@@ -221,6 +276,24 @@ sentry-cli upload-dsym path/to/dsym --include-sources
221
276
  expect(findDebugFilesUploadPhase(input)).toEqual(expected);
222
277
  });
223
278
 
279
+ it('returns debug files build phase using bundled scripts', () => {
280
+ const input = {
281
+ 1: {
282
+ shellScript: 'foo',
283
+ },
284
+ 2: {
285
+ shellScript: `/bin/sh ../node_modules/@sentry/react-native/scripts/sentry-xcode-debug-files.sh`,
286
+ },
287
+ };
288
+ const expected = [
289
+ '2',
290
+ {
291
+ shellScript: `/bin/sh ../node_modules/@sentry/react-native/scripts/sentry-xcode-debug-files.sh`,
292
+ },
293
+ ];
294
+ expect(findDebugFilesUploadPhase(input)).toEqual(expected);
295
+ });
296
+
224
297
  it('returns undefined if build phase not present', () => {
225
298
  const input = {
226
299
  1: {