@sentry/wizard 3.11.0 → 3.13.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 (91) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/lib/Steps/ChooseIntegration.js +1 -0
  3. package/dist/lib/Steps/ChooseIntegration.js.map +1 -1
  4. package/dist/package.json +1 -1
  5. package/dist/src/android/android-wizard.js +14 -4
  6. package/dist/src/android/android-wizard.js.map +1 -1
  7. package/dist/src/android/code-tools.d.ts +8 -0
  8. package/dist/src/android/code-tools.js +20 -8
  9. package/dist/src/android/code-tools.js.map +1 -1
  10. package/dist/src/android/gradle.js +6 -1
  11. package/dist/src/android/gradle.js.map +1 -1
  12. package/dist/src/nextjs/nextjs-wizard.js +5 -2
  13. package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
  14. package/dist/src/nextjs/templates.d.ts +1 -1
  15. package/dist/src/nextjs/templates.js +2 -2
  16. package/dist/src/nextjs/templates.js.map +1 -1
  17. package/dist/src/remix/remix-wizard.js +8 -4
  18. package/dist/src/remix/remix-wizard.js.map +1 -1
  19. package/dist/src/remix/sdk-setup.d.ts +5 -1
  20. package/dist/src/remix/sdk-setup.js +3 -2
  21. package/dist/src/remix/sdk-setup.js.map +1 -1
  22. package/dist/src/sourcemaps/tools/sentry-cli.d.ts +9 -0
  23. package/dist/src/sourcemaps/tools/sentry-cli.js +26 -22
  24. package/dist/src/sourcemaps/tools/sentry-cli.js.map +1 -1
  25. package/dist/src/sourcemaps/tools/tsc.d.ts +6 -0
  26. package/dist/src/sourcemaps/tools/tsc.js +98 -17
  27. package/dist/src/sourcemaps/tools/tsc.js.map +1 -1
  28. package/dist/src/sourcemaps/tools/vite.js +39 -124
  29. package/dist/src/sourcemaps/tools/vite.js.map +1 -1
  30. package/dist/src/sourcemaps/tools/webpack.d.ts +6 -1
  31. package/dist/src/sourcemaps/tools/webpack.js +280 -25
  32. package/dist/src/sourcemaps/tools/webpack.js.map +1 -1
  33. package/dist/src/sveltekit/sdk-setup.js +123 -49
  34. package/dist/src/sveltekit/sdk-setup.js.map +1 -1
  35. package/dist/src/sveltekit/sveltekit-wizard.d.ts +1 -0
  36. package/dist/src/sveltekit/sveltekit-wizard.js +119 -44
  37. package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
  38. package/dist/src/sveltekit/utils.d.ts +2 -0
  39. package/dist/src/sveltekit/utils.js +48 -0
  40. package/dist/src/sveltekit/utils.js.map +1 -0
  41. package/dist/src/utils/ast-utils.d.ts +77 -3
  42. package/dist/src/utils/ast-utils.js +172 -6
  43. package/dist/src/utils/ast-utils.js.map +1 -1
  44. package/dist/src/utils/clack-utils.d.ts +85 -1
  45. package/dist/src/utils/clack-utils.js +214 -51
  46. package/dist/src/utils/clack-utils.js.map +1 -1
  47. package/dist/src/utils/package-manager.d.ts +5 -0
  48. package/dist/src/utils/package-manager.js +11 -7
  49. package/dist/src/utils/package-manager.js.map +1 -1
  50. package/dist/test/android/code-tools.test.d.ts +1 -0
  51. package/dist/test/android/code-tools.test.js +34 -0
  52. package/dist/test/android/code-tools.test.js.map +1 -0
  53. package/dist/test/sourcemaps/tools/sentry-cli.test.d.ts +1 -0
  54. package/dist/test/sourcemaps/tools/sentry-cli.test.js +112 -0
  55. package/dist/test/sourcemaps/tools/sentry-cli.test.js.map +1 -0
  56. package/dist/test/sourcemaps/tools/tsc.test.d.ts +1 -0
  57. package/dist/test/sourcemaps/tools/tsc.test.js +121 -0
  58. package/dist/test/sourcemaps/tools/tsc.test.js.map +1 -0
  59. package/dist/test/sourcemaps/tools/webpack.test.d.ts +1 -0
  60. package/dist/test/sourcemaps/tools/webpack.test.js +179 -0
  61. package/dist/test/sourcemaps/tools/webpack.test.js.map +1 -0
  62. package/dist/test/utils/ast-utils.test.js +181 -15
  63. package/dist/test/utils/ast-utils.test.js.map +1 -1
  64. package/dist/test/utils/clack-utils.test.d.ts +1 -0
  65. package/dist/test/utils/clack-utils.test.js +200 -0
  66. package/dist/test/utils/clack-utils.test.js.map +1 -0
  67. package/lib/Steps/ChooseIntegration.ts +1 -0
  68. package/package.json +1 -1
  69. package/src/android/android-wizard.ts +16 -5
  70. package/src/android/code-tools.ts +21 -7
  71. package/src/android/gradle.ts +6 -1
  72. package/src/nextjs/nextjs-wizard.ts +15 -3
  73. package/src/nextjs/templates.ts +3 -2
  74. package/src/remix/remix-wizard.ts +8 -11
  75. package/src/remix/sdk-setup.ts +8 -2
  76. package/src/sourcemaps/tools/sentry-cli.ts +16 -9
  77. package/src/sourcemaps/tools/tsc.ts +133 -28
  78. package/src/sourcemaps/tools/vite.ts +37 -127
  79. package/src/sourcemaps/tools/webpack.ts +343 -27
  80. package/src/sveltekit/sdk-setup.ts +115 -39
  81. package/src/sveltekit/sveltekit-wizard.ts +93 -25
  82. package/src/sveltekit/utils.ts +50 -0
  83. package/src/utils/ast-utils.ts +203 -7
  84. package/src/utils/clack-utils.ts +211 -44
  85. package/src/utils/package-manager.ts +12 -6
  86. package/test/android/code-tools.test.ts +49 -0
  87. package/test/sourcemaps/tools/sentry-cli.test.ts +51 -0
  88. package/test/sourcemaps/tools/tsc.test.ts +181 -0
  89. package/test/sourcemaps/tools/webpack.test.ts +303 -0
  90. package/test/utils/ast-utils.test.ts +240 -20
  91. package/test/utils/clack-utils.test.ts +142 -0
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ var __generator = (this && this.__generator) || function (thisArg, body) {
35
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
36
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
37
+ function verb(n) { return function (v) { return step([n, v]); }; }
38
+ function step(op) {
39
+ if (f) throw new TypeError("Generator is already executing.");
40
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
41
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
42
+ if (y = 0, t) op = [op[0] & 2, t.value];
43
+ switch (op[0]) {
44
+ case 0: case 1: t = op; break;
45
+ case 4: _.label++; return { value: op[1], done: false };
46
+ case 5: _.label++; y = op[1]; op = [0]; continue;
47
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
48
+ default:
49
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
50
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
51
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
52
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
53
+ if (t[2]) _.ops.pop();
54
+ _.trys.pop(); continue;
55
+ }
56
+ op = body.call(thisArg, _);
57
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
58
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
59
+ }
60
+ };
61
+ Object.defineProperty(exports, "__esModule", { value: true });
62
+ var clack_utils_1 = require("../../src/utils/clack-utils");
63
+ var fs = __importStar(require("fs"));
64
+ var clackMock;
65
+ jest.mock('@clack/prompts', function () {
66
+ clackMock = {
67
+ log: {
68
+ info: jest.fn(),
69
+ success: jest.fn(),
70
+ warn: jest.fn(),
71
+ },
72
+ text: jest.fn(),
73
+ confirm: jest.fn(),
74
+ cancel: jest.fn(),
75
+ // passthrough for abortIfCancelled
76
+ isCancel: jest.fn().mockReturnValue(false),
77
+ };
78
+ return clackMock;
79
+ });
80
+ function mockUserResponse(fn, response) {
81
+ fn.mockReturnValueOnce(response);
82
+ }
83
+ describe('askForToolConfigPath', function () {
84
+ afterEach(function () {
85
+ jest.clearAllMocks();
86
+ });
87
+ it('returns undefined if users have no config file', function () { return __awaiter(void 0, void 0, void 0, function () {
88
+ var result;
89
+ return __generator(this, function (_a) {
90
+ switch (_a.label) {
91
+ case 0:
92
+ mockUserResponse(clackMock.confirm, false);
93
+ return [4 /*yield*/, (0, clack_utils_1.askForToolConfigPath)('Webpack', 'webpack.config.js')];
94
+ case 1:
95
+ result = _a.sent();
96
+ expect(clackMock.confirm).toHaveBeenCalledWith(expect.objectContaining({
97
+ message: expect.stringContaining('have a Webpack config file'),
98
+ }));
99
+ expect(result).toBeUndefined();
100
+ return [2 /*return*/];
101
+ }
102
+ });
103
+ }); });
104
+ it('returns the path if users have a config file and the entered path is valid', function () { return __awaiter(void 0, void 0, void 0, function () {
105
+ var result;
106
+ return __generator(this, function (_a) {
107
+ switch (_a.label) {
108
+ case 0:
109
+ mockUserResponse(clackMock.confirm, true);
110
+ mockUserResponse(clackMock.text, 'my.webpack.config.js');
111
+ return [4 /*yield*/, (0, clack_utils_1.askForToolConfigPath)('Webpack', 'webpack.config.js')];
112
+ case 1:
113
+ result = _a.sent();
114
+ expect(clackMock.confirm).toHaveBeenCalledWith(expect.objectContaining({
115
+ message: expect.stringContaining('have a Webpack config file'),
116
+ }));
117
+ expect(clackMock.text).toHaveBeenCalledWith(expect.objectContaining({
118
+ message: expect.stringContaining('enter the path to your Webpack config file'),
119
+ }));
120
+ expect(result).toBe('my.webpack.config.js');
121
+ return [2 /*return*/];
122
+ }
123
+ });
124
+ }); });
125
+ });
126
+ describe('createNewConfigFile', function () {
127
+ afterEach(function () {
128
+ jest.clearAllMocks();
129
+ });
130
+ it('writes the file to disk and returns true if the file was created successfully', function () { return __awaiter(void 0, void 0, void 0, function () {
131
+ var writeFileSpy, filename, code, result;
132
+ return __generator(this, function (_a) {
133
+ switch (_a.label) {
134
+ case 0:
135
+ writeFileSpy = jest
136
+ .spyOn(fs.promises, 'writeFile')
137
+ .mockImplementation(jest.fn());
138
+ filename = '/weboack.config.js';
139
+ code = "module.exports = {/*config...*/}";
140
+ return [4 /*yield*/, (0, clack_utils_1.createNewConfigFile)(filename, code)];
141
+ case 1:
142
+ result = _a.sent();
143
+ expect(result).toBe(true);
144
+ expect(writeFileSpy).toHaveBeenCalledWith(filename, code);
145
+ return [2 /*return*/];
146
+ }
147
+ });
148
+ }); });
149
+ it('logs more information if provided as an argument', function () { return __awaiter(void 0, void 0, void 0, function () {
150
+ var filename, code, moreInfo;
151
+ return __generator(this, function (_a) {
152
+ switch (_a.label) {
153
+ case 0:
154
+ jest.spyOn(fs.promises, 'writeFile').mockImplementation(jest.fn());
155
+ filename = '/weboack.config.js';
156
+ code = "module.exports = {/*config...*/}";
157
+ moreInfo = 'More information...';
158
+ return [4 /*yield*/, (0, clack_utils_1.createNewConfigFile)(filename, code, moreInfo)];
159
+ case 1:
160
+ _a.sent();
161
+ expect(clackMock.log.info).toHaveBeenCalledTimes(1);
162
+ expect(clackMock.log.info).toHaveBeenCalledWith(expect.stringContaining(moreInfo));
163
+ return [2 /*return*/];
164
+ }
165
+ });
166
+ }); });
167
+ it('returns false and logs a warning if the file could not be created', function () { return __awaiter(void 0, void 0, void 0, function () {
168
+ var writeFileSpy, filename, code, result;
169
+ return __generator(this, function (_a) {
170
+ switch (_a.label) {
171
+ case 0:
172
+ writeFileSpy = jest
173
+ .spyOn(fs.promises, 'writeFile')
174
+ .mockImplementation(function () { return Promise.reject(new Error('Could not write')); });
175
+ filename = '/webpack.config.js';
176
+ code = "module.exports = {/*config...*/}";
177
+ return [4 /*yield*/, (0, clack_utils_1.createNewConfigFile)(filename, code)];
178
+ case 1:
179
+ result = _a.sent();
180
+ expect(result).toBe(false);
181
+ expect(writeFileSpy).toHaveBeenCalledWith(filename, code);
182
+ expect(clackMock.log.warn).toHaveBeenCalledTimes(1);
183
+ return [2 /*return*/];
184
+ }
185
+ });
186
+ }); });
187
+ it('returns false if the passed path is not absolute', function () { return __awaiter(void 0, void 0, void 0, function () {
188
+ var result;
189
+ return __generator(this, function (_a) {
190
+ switch (_a.label) {
191
+ case 0: return [4 /*yield*/, (0, clack_utils_1.createNewConfigFile)('./relative/webpack.config.js', '')];
192
+ case 1:
193
+ result = _a.sent();
194
+ expect(result).toBe(false);
195
+ return [2 /*return*/];
196
+ }
197
+ });
198
+ }); });
199
+ });
200
+ //# sourceMappingURL=clack-utils.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clack-utils.test.js","sourceRoot":"","sources":["../../../test/utils/clack-utils.test.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n askForToolConfigPath,\n createNewConfigFile,\n} from '../../src/utils/clack-utils';\n\nimport * as fs from 'fs';\n\ntype ClackMock = {\n confirm: jest.Mock;\n text: jest.Mock;\n isCancel: jest.Mock;\n cancel: jest.Mock;\n log: {\n info: jest.Mock;\n success: jest.Mock;\n warn: jest.Mock;\n };\n};\n\nlet clackMock: ClackMock;\n\njest.mock('@clack/prompts', () => {\n clackMock = {\n log: {\n info: jest.fn(),\n success: jest.fn(),\n warn: jest.fn(),\n },\n text: jest.fn(),\n confirm: jest.fn(),\n cancel: jest.fn(),\n // passthrough for abortIfCancelled\n isCancel: jest.fn().mockReturnValue(false),\n };\n return clackMock;\n});\n\nfunction mockUserResponse(fn: jest.Mock, response: any) {\n fn.mockReturnValueOnce(response);\n}\n\ndescribe('askForToolConfigPath', () => {\n afterEach(() => {\n jest.clearAllMocks();\n });\n\n it('returns undefined if users have no config file', async () => {\n mockUserResponse(clackMock.confirm, false);\n\n const result = await askForToolConfigPath('Webpack', 'webpack.config.js');\n\n expect(clackMock.confirm).toHaveBeenCalledWith(\n expect.objectContaining({\n message: expect.stringContaining('have a Webpack config file'),\n }),\n );\n\n expect(result).toBeUndefined();\n });\n\n it('returns the path if users have a config file and the entered path is valid', async () => {\n mockUserResponse(clackMock.confirm, true);\n mockUserResponse(clackMock.text, 'my.webpack.config.js');\n\n const result = await askForToolConfigPath('Webpack', 'webpack.config.js');\n\n expect(clackMock.confirm).toHaveBeenCalledWith(\n expect.objectContaining({\n message: expect.stringContaining('have a Webpack config file'),\n }),\n );\n\n expect(clackMock.text).toHaveBeenCalledWith(\n expect.objectContaining({\n message: expect.stringContaining(\n 'enter the path to your Webpack config file',\n ),\n }),\n );\n\n expect(result).toBe('my.webpack.config.js');\n });\n});\n\ndescribe('createNewConfigFile', () => {\n afterEach(() => {\n jest.clearAllMocks();\n });\n\n it('writes the file to disk and returns true if the file was created successfully', async () => {\n const writeFileSpy = jest\n .spyOn(fs.promises, 'writeFile')\n .mockImplementation(jest.fn());\n\n const filename = '/weboack.config.js';\n const code = `module.exports = {/*config...*/}`;\n\n const result = await createNewConfigFile(filename, code);\n\n expect(result).toBe(true);\n expect(writeFileSpy).toHaveBeenCalledWith(filename, code);\n });\n\n it('logs more information if provided as an argument', async () => {\n jest.spyOn(fs.promises, 'writeFile').mockImplementation(jest.fn());\n\n const filename = '/weboack.config.js';\n const code = `module.exports = {/*config...*/}`;\n const moreInfo = 'More information...';\n\n await createNewConfigFile(filename, code, moreInfo);\n\n expect(clackMock.log.info).toHaveBeenCalledTimes(1);\n expect(clackMock.log.info).toHaveBeenCalledWith(\n expect.stringContaining(moreInfo),\n );\n });\n\n it('returns false and logs a warning if the file could not be created', async () => {\n const writeFileSpy = jest\n .spyOn(fs.promises, 'writeFile')\n .mockImplementation(() => Promise.reject(new Error('Could not write')));\n\n const filename = '/webpack.config.js';\n const code = `module.exports = {/*config...*/}`;\n\n const result = await createNewConfigFile(filename, code);\n\n expect(result).toBe(false);\n expect(writeFileSpy).toHaveBeenCalledWith(filename, code);\n expect(clackMock.log.warn).toHaveBeenCalledTimes(1);\n });\n\n it('returns false if the passed path is not absolute', async () => {\n const result = await createNewConfigFile(\n './relative/webpack.config.js',\n '',\n );\n\n expect(result).toBe(false);\n });\n});\n"]}
@@ -113,6 +113,7 @@ export class ChooseIntegration extends BaseStep {
113
113
  message: 'What platform do you want to set up?',
114
114
  name: 'integration',
115
115
  type: 'list',
116
+ pageSize: 10,
116
117
  },
117
118
  ]);
118
119
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/wizard",
3
- "version": "3.11.0",
3
+ "version": "3.13.0",
4
4
  "homepage": "https://github.com/getsentry/sentry-wizard",
5
5
  "repository": "https://github.com/getsentry/sentry-wizard",
6
6
  "description": "Sentry wizard helping you to configure your project",
@@ -3,6 +3,7 @@ import * as fs from 'fs';
3
3
  // @ts-ignore - clack is ESM and TS complains about that. It works though
4
4
  import * as clack from '@clack/prompts';
5
5
  import * as path from 'path';
6
+ import * as Sentry from '@sentry/node';
6
7
  import * as gradle from './gradle';
7
8
  import * as manifest from './manifest';
8
9
  import * as codetools from './code-tools';
@@ -69,6 +70,7 @@ async function runAndroidWizardWithTelemetry(
69
70
  clack.log.error(
70
71
  'No Gradle project found. Please run this command from the root of your project.',
71
72
  );
73
+ Sentry.captureException('No Gradle project found');
72
74
  await abort();
73
75
  return;
74
76
  }
@@ -77,10 +79,8 @@ async function runAndroidWizardWithTelemetry(
77
79
  gradle.selectAppFile(buildGradleFiles),
78
80
  );
79
81
 
80
- const { selectedProject, authToken } = await getOrAskForProjectData(
81
- options,
82
- 'android',
83
- );
82
+ const { selectedProject, selfHosted, sentryUrl, authToken } =
83
+ await getOrAskForProjectData(options, 'android');
84
84
 
85
85
  // ======== STEP 1. Add Sentry Gradle Plugin to build.gradle(.kts) ============
86
86
  clack.log.step(
@@ -100,6 +100,7 @@ async function runAndroidWizardWithTelemetry(
100
100
  "Could not add Sentry Gradle plugin to your app's build.gradle file. You'll have to add it manually.\nPlease follow the instructions at https://docs.sentry.io/platforms/android/#install",
101
101
  );
102
102
  }
103
+ Sentry.setTag('gradle-plugin-added', pluginAdded);
103
104
 
104
105
  // ======== STEP 2. Configure Sentry SDK via AndroidManifest ============
105
106
  clack.log.step(
@@ -119,6 +120,7 @@ async function runAndroidWizardWithTelemetry(
119
120
  "Could not configure the Sentry SDK. You'll have to do it manually.\nPlease follow the instructions at https://docs.sentry.io/platforms/android/#configure",
120
121
  );
121
122
  }
123
+ Sentry.setTag('android-manifest-updated', manifestUpdated);
122
124
 
123
125
  // ======== STEP 3. Patch Main Activity with a test error snippet ============
124
126
  clack.log.step(
@@ -133,10 +135,13 @@ async function runAndroidWizardWithTelemetry(
133
135
  packageName = gradle.getNamespace(appFile);
134
136
  }
135
137
  const activityName = mainActivity.activityName;
138
+ Sentry.setTag('has-activity-name', !!activityName);
139
+ Sentry.setTag('has-package-name', !!packageName);
136
140
  if (!activityName || !packageName) {
137
141
  clack.log.warn(
138
142
  "Could not find Activity with intent action MAIN. You'll have to manually verify the setup.\nPlease follow the instructions at https://docs.sentry.io/platforms/android/#verify",
139
143
  );
144
+ Sentry.captureException('Could not find Main Activity');
140
145
  } else {
141
146
  const packageNameStable = packageName;
142
147
  const activityFile = traceStep('Find Main Activity Source File', () =>
@@ -151,6 +156,7 @@ async function runAndroidWizardWithTelemetry(
151
156
  "Could not patch main activity. You'll have to manually verify the setup.\nPlease follow the instructions at https://docs.sentry.io/platforms/android/#verify",
152
157
  );
153
158
  }
159
+ Sentry.setTag('main-activity-patched', activityPatched);
154
160
  }
155
161
 
156
162
  // ======== STEP 4. Add sentry-cli config file ============
@@ -164,11 +170,16 @@ async function runAndroidWizardWithTelemetry(
164
170
  );
165
171
 
166
172
  // ======== OUTRO ========
173
+ const issuesPageLink = selfHosted
174
+ ? `${sentryUrl}organizations/${selectedProject.organization.slug}/issues/?project=${selectedProject.id}`
175
+ : `https://${selectedProject.organization.slug}.sentry.io/issues/?project=${selectedProject.id}`;
176
+
167
177
  clack.outro(`
168
178
  ${chalk.greenBright('Successfully installed the Sentry Android SDK!')}
169
179
 
170
180
  ${chalk.cyan(
171
- 'You can validate your setup by launching your application and checking Sentry issues page afterwards',
181
+ `You can validate your setup by launching your application and checking Sentry issues page afterwards
182
+ ${issuesPageLink}`,
172
183
  )}
173
184
 
174
185
  Check out the SDK documentation for further configuration:
@@ -103,13 +103,8 @@ export function patchMainActivity(activityFile: string | undefined): boolean {
103
103
  return true;
104
104
  }
105
105
 
106
- const importRegex = /import\s+[\w.]+;?/gim;
107
- let importsMatch = importRegex.exec(activityContent);
108
- let importIndex = 0;
109
- while (importsMatch) {
110
- importIndex = importsMatch.index + importsMatch[0].length + 1;
111
- importsMatch = importRegex.exec(activityContent);
112
- }
106
+ const importIndex = getLastImportLineLocation(activityContent);
107
+
113
108
  let newActivityContent;
114
109
  if (activityFile.endsWith('.kt')) {
115
110
  newActivityContent =
@@ -154,3 +149,22 @@ export function patchMainActivity(activityFile: string | undefined): boolean {
154
149
 
155
150
  return true;
156
151
  }
152
+
153
+ /**
154
+ * Returns the string index of the last import statement in the given code file.
155
+ * Works for both Java and Kotlin import statements.
156
+ *
157
+ * @param sourceCode
158
+ * @returns the insert index, or 0 if none found.
159
+ */
160
+ export function getLastImportLineLocation(sourceCode: string): number {
161
+ const importRegex = /import(?:\sstatic)?\s+[\w.*]+(?: as [\w.]+)?;?/gim;
162
+
163
+ let importsMatch = importRegex.exec(sourceCode);
164
+ let importIndex = 0;
165
+ while (importsMatch) {
166
+ importIndex = importsMatch.index + importsMatch[0].length + 1;
167
+ importsMatch = importRegex.exec(sourceCode);
168
+ }
169
+ return importIndex;
170
+ }
@@ -48,7 +48,12 @@ export async function selectAppFile(
48
48
  const appFile = await abortIfCancelled(
49
49
  clack.text({
50
50
  message: `Unable to find your app's directory.
51
- Please enter the relative path to your app's build.gradle file from the root project (e.g. "app/build.gradle.kts")`,
51
+ Please enter the relative path to your app's build.gradle file from the root project`,
52
+ placeholder: 'app/build.gradle.kts',
53
+ validate(value) {
54
+ if (!value.includes('.gradle') || !fs.existsSync(value))
55
+ return `Not a valid gradle file.`;
56
+ },
52
57
  }),
53
58
  );
54
59
  return appFile;
@@ -118,6 +118,8 @@ export async function runNextjsWizard(options: WizardOptions): Promise<void> {
118
118
  const sentryWebpackOptionsTemplate = getNextjsWebpackPluginOptionsTemplate(
119
119
  selectedProject.organization.slug,
120
120
  selectedProject.slug,
121
+ selfHosted,
122
+ sentryUrl,
121
123
  );
122
124
  const sentryBuildOptionsTemplate = getNextjsSentryBuildOptionsTemplate();
123
125
 
@@ -323,6 +325,13 @@ export async function runNextjsWizard(options: WizardOptions): Promise<void> {
323
325
  useClient: true,
324
326
  });
325
327
 
328
+ fs.mkdirSync(
329
+ path.join(process.cwd(), ...appLocation, 'sentry-example-page'),
330
+ {
331
+ recursive: true,
332
+ },
333
+ );
334
+
326
335
  await fs.promises.writeFile(
327
336
  path.join(
328
337
  process.cwd(),
@@ -340,9 +349,12 @@ export async function runNextjsWizard(options: WizardOptions): Promise<void> {
340
349
  )}.`,
341
350
  );
342
351
 
343
- fs.mkdirSync(path.join(process.cwd(), ...appLocation, 'api'), {
344
- recursive: true,
345
- });
352
+ fs.mkdirSync(
353
+ path.join(process.cwd(), ...appLocation, 'api', 'sentry-example-api'),
354
+ {
355
+ recursive: true,
356
+ },
357
+ );
346
358
 
347
359
  await fs.promises.writeFile(
348
360
  path.join(
@@ -1,6 +1,8 @@
1
1
  export function getNextjsWebpackPluginOptionsTemplate(
2
2
  orgSlug: string,
3
3
  projectSlug: string,
4
+ selfHosted: boolean,
5
+ url: string,
4
6
  ): string {
5
7
  return `{
6
8
  // For all available options, see:
@@ -8,9 +10,8 @@ export function getNextjsWebpackPluginOptionsTemplate(
8
10
 
9
11
  // Suppresses source map uploading logs during build
10
12
  silent: true,
11
-
12
13
  org: "${orgSlug}",
13
- project: "${projectSlug}",
14
+ project: "${projectSlug}",${selfHosted ? `\n url: "${url}"` : ''}
14
15
  }`;
15
16
  }
16
17
 
@@ -53,10 +53,8 @@ async function runRemixWizardWithTelemetry(
53
53
  // We expect `@remix-run/dev` to be installed for every Remix project
54
54
  await ensurePackageIsInstalled(packageJson, '@remix-run/dev', 'Remix');
55
55
 
56
- const { selectedProject, authToken } = await getOrAskForProjectData(
57
- options,
58
- 'javascript-remix',
59
- );
56
+ const { selectedProject, authToken, sentryUrl } =
57
+ await getOrAskForProjectData(options, 'javascript-remix');
60
58
 
61
59
  await traceStep('Install Sentry SDK', () =>
62
60
  installPackage({
@@ -70,16 +68,15 @@ async function runRemixWizardWithTelemetry(
70
68
  const isTS = isUsingTypeScript();
71
69
  const isV2 = isRemixV2(remixConfig, packageJson);
72
70
 
73
- await addSentryCliConfig(
74
- authToken,
75
- sourceMapsCliSetupConfig,
76
- selectedProject.organization.slug,
77
- selectedProject.name,
78
- );
71
+ await addSentryCliConfig(authToken, sourceMapsCliSetupConfig);
79
72
 
80
73
  await traceStep('Update build script for sourcemap uploads', async () => {
81
74
  try {
82
- await updateBuildScript();
75
+ await updateBuildScript({
76
+ org: selectedProject.organization.slug,
77
+ project: selectedProject.name,
78
+ url: sentryUrl,
79
+ });
83
80
  } catch (e) {
84
81
  clack.log
85
82
  .warn(`Could not update build script to generate and upload sourcemaps.
@@ -151,7 +151,11 @@ export async function instrumentRootRoute(
151
151
  /* eslint-enable @typescript-eslint/no-unsafe-member-access */
152
152
  }
153
153
 
154
- export async function updateBuildScript(): Promise<void> {
154
+ export async function updateBuildScript(args: {
155
+ org: string;
156
+ project: string;
157
+ url?: string;
158
+ }): Promise<void> {
155
159
  /* eslint-disable @typescript-eslint/no-unsafe-member-access */
156
160
  // Add sourcemaps option to build script
157
161
  const packageJsonPath = path.join(process.cwd(), 'package.json');
@@ -166,7 +170,9 @@ export async function updateBuildScript(): Promise<void> {
166
170
 
167
171
  if (!packageJson.scripts.build) {
168
172
  packageJson.scripts.build =
169
- 'remix build --sourcemap && sentry-upload-sourcemaps';
173
+ `remix build --sourcemap && sentry-upload-sourcemaps --org ${args.org} --project ${args.project}` +
174
+ (args.url ? ` --url ${args.url}` : '');
175
+
170
176
  // eslint-disable-next-line @typescript-eslint/no-unsafe-call
171
177
  } else if (packageJson.scripts.build.includes('remix build')) {
172
178
  // eslint-disable-next-line @typescript-eslint/no-unsafe-call
@@ -1,5 +1,5 @@
1
1
  // @ts-ignore - clack is ESM and TS complains about that. It works though
2
- import clack from '@clack/prompts';
2
+ import * as clack from '@clack/prompts';
3
3
  import chalk from 'chalk';
4
4
  import * as Sentry from '@sentry/node';
5
5
  import * as path from 'path';
@@ -14,7 +14,7 @@ import {
14
14
  import { SourceMapUploadToolConfigurationOptions } from './types';
15
15
  import { hasPackageInstalled, PackageDotJson } from '../../utils/package-json';
16
16
  import { traceStep } from '../../telemetry';
17
- import { detectPackageManger } from '../../utils/package-manager';
17
+ import { detectPackageManger, NPM } from '../../utils/package-manager';
18
18
 
19
19
  const SENTRY_NPM_SCRIPT_NAME = 'sentry:sourcemaps';
20
20
 
@@ -194,7 +194,7 @@ async function askShouldAddToBuildCommand(): Promise<boolean> {
194
194
  *
195
195
  * @param packageDotJson The package.json which will be modified.
196
196
  */
197
- async function addSentryCommandToBuildCommand(
197
+ export async function addSentryCommandToBuildCommand(
198
198
  packageDotJson: PackageDotJson,
199
199
  ): Promise<void> {
200
200
  // This usually shouldn't happen because earlier we added the
@@ -205,8 +205,7 @@ async function addSentryCommandToBuildCommand(
205
205
  (s) => s !== SENTRY_NPM_SCRIPT_NAME,
206
206
  );
207
207
 
208
- const packageManager = detectPackageManger();
209
- const packageManagerName = packageManager?.name ?? 'npm';
208
+ const packageManager = detectPackageManger() ?? NPM;
210
209
 
211
210
  // Heuristic to pre-select the build command:
212
211
  // Often, 'build' is the prod build command, so we favour it.
@@ -221,7 +220,7 @@ async function addSentryCommandToBuildCommand(
221
220
  (await abortIfCancelled(
222
221
  clack.confirm({
223
222
  message: `Is ${chalk.cyan(
224
- `${packageManagerName} run ${buildCommand}`,
223
+ `${packageManager.runScriptCommand} ${buildCommand}`,
225
224
  )} your production build command?`,
226
225
  }),
227
226
  ));
@@ -229,7 +228,7 @@ async function addSentryCommandToBuildCommand(
229
228
  if (allNpmScripts.length && (!buildCommand || !isProdBuildCommand)) {
230
229
  buildCommand = await abortIfCancelled(
231
230
  clack.select({
232
- message: `Which ${packageManagerName} command in your ${chalk.cyan(
231
+ message: `Which ${packageManager.name} command in your ${chalk.cyan(
233
232
  'package.json',
234
233
  )} builds your application for production?`,
235
234
  options: allNpmScripts
@@ -252,10 +251,18 @@ Please add it manually to your prod build command.`,
252
251
  return;
253
252
  }
254
253
 
254
+ const oldCommand = packageDotJson.scripts[buildCommand];
255
+ if (!oldCommand) {
256
+ // very unlikely to happen but nevertheless
257
+ clack.log.warn(
258
+ `\`${buildCommand}\` doesn't seem to be part of your package.json scripts`,
259
+ );
260
+ return;
261
+ }
262
+
255
263
  packageDotJson.scripts[
256
264
  buildCommand
257
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
258
- ] = `${packageDotJson.scripts[buildCommand]} && ${packageManager} run ${SENTRY_NPM_SCRIPT_NAME}`;
265
+ ] = `${oldCommand} && ${packageManager.runScriptCommand} ${SENTRY_NPM_SCRIPT_NAME}`;
259
266
 
260
267
  await fs.promises.writeFile(
261
268
  path.join(process.cwd(), 'package.json'),