@sentry/wizard 3.11.0 → 3.12.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 (46) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/package.json +1 -1
  3. package/dist/src/android/android-wizard.js +8 -0
  4. package/dist/src/android/android-wizard.js.map +1 -1
  5. package/dist/src/android/code-tools.d.ts +8 -0
  6. package/dist/src/android/code-tools.js +20 -8
  7. package/dist/src/android/code-tools.js.map +1 -1
  8. package/dist/src/android/gradle.js +6 -1
  9. package/dist/src/android/gradle.js.map +1 -1
  10. package/dist/src/sourcemaps/tools/vite.js +36 -111
  11. package/dist/src/sourcemaps/tools/vite.js.map +1 -1
  12. package/dist/src/sourcemaps/tools/webpack.d.ts +6 -1
  13. package/dist/src/sourcemaps/tools/webpack.js +290 -25
  14. package/dist/src/sourcemaps/tools/webpack.js.map +1 -1
  15. package/dist/src/sveltekit/sdk-setup.js +2 -2
  16. package/dist/src/sveltekit/sdk-setup.js.map +1 -1
  17. package/dist/src/utils/ast-utils.d.ts +7 -3
  18. package/dist/src/utils/ast-utils.js +20 -5
  19. package/dist/src/utils/ast-utils.js.map +1 -1
  20. package/dist/src/utils/clack-utils.d.ts +52 -0
  21. package/dist/src/utils/clack-utils.js +169 -12
  22. package/dist/src/utils/clack-utils.js.map +1 -1
  23. package/dist/test/android/code-tools.test.d.ts +1 -0
  24. package/dist/test/android/code-tools.test.js +34 -0
  25. package/dist/test/android/code-tools.test.js.map +1 -0
  26. package/dist/test/sourcemaps/tools/webpack.test.d.ts +1 -0
  27. package/dist/test/sourcemaps/tools/webpack.test.js +179 -0
  28. package/dist/test/sourcemaps/tools/webpack.test.js.map +1 -0
  29. package/dist/test/utils/ast-utils.test.js +42 -7
  30. package/dist/test/utils/ast-utils.test.js.map +1 -1
  31. package/dist/test/utils/clack-utils.test.d.ts +1 -0
  32. package/dist/test/utils/clack-utils.test.js +200 -0
  33. package/dist/test/utils/clack-utils.test.js.map +1 -0
  34. package/package.json +1 -1
  35. package/src/android/android-wizard.ts +8 -0
  36. package/src/android/code-tools.ts +21 -7
  37. package/src/android/gradle.ts +6 -1
  38. package/src/sourcemaps/tools/vite.ts +22 -88
  39. package/src/sourcemaps/tools/webpack.ts +369 -30
  40. package/src/sveltekit/sdk-setup.ts +6 -2
  41. package/src/utils/ast-utils.ts +23 -7
  42. package/src/utils/clack-utils.ts +150 -2
  43. package/test/android/code-tools.test.ts +49 -0
  44. package/test/sourcemaps/tools/webpack.test.ts +303 -0
  45. package/test/utils/ast-utils.test.ts +28 -9
  46. package/test/utils/clack-utils.test.ts +142 -0
@@ -0,0 +1,179 @@
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 fs = __importStar(require("fs"));
63
+ var webpack_1 = require("../../../src/sourcemaps/tools/webpack");
64
+ function updateFileContent(content) {
65
+ fileContent = content;
66
+ }
67
+ var fileContent = '';
68
+ jest.mock('@clack/prompts', function () {
69
+ return {
70
+ log: {
71
+ info: jest.fn(),
72
+ success: jest.fn(),
73
+ },
74
+ select: jest.fn().mockImplementation(function () { return Promise.resolve(true); }),
75
+ isCancel: jest.fn().mockReturnValue(false),
76
+ };
77
+ });
78
+ jest
79
+ .spyOn(fs.promises, 'readFile')
80
+ .mockImplementation(function () { return Promise.resolve(fileContent); });
81
+ var writeFileSpy = jest
82
+ .spyOn(fs.promises, 'writeFile')
83
+ .mockImplementation(function () { return Promise.resolve(void 0); });
84
+ var noSourcemapNoPluginsPojo = "module.exports = {\n entry: \"./src/index.js\",\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n};";
85
+ var noSourcemapNoPluginsPojoResult = "const {\n sentryWebpackPlugin\n} = require(\"@sentry/webpack-plugin\");\n\nmodule.exports = {\n entry: \"./src/index.js\",\n\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n\n devtool: \"source-map\",\n\n plugins: [sentryWebpackPlugin({\n authToken: process.env.SENTRY_AUTH_TOKEN,\n org: \"my-org\",\n project: \"my-project\"\n })]\n};";
86
+ var noSourcemapsNoPluginsId = "const config = {\n entry: \"./src/index.js\",\n\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n};\n\nmodule.exports = config;";
87
+ var noSourcemapsNoPluginsIdResult = "const {\n sentryWebpackPlugin\n} = require(\"@sentry/webpack-plugin\");\n\nconst config = {\n entry: \"./src/index.js\",\n\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n\n devtool: \"source-map\",\n\n plugins: [sentryWebpackPlugin({\n authToken: process.env.SENTRY_AUTH_TOKEN,\n org: \"my-org\",\n project: \"my-project\"\n })]\n};\n\nmodule.exports = config;";
88
+ var hiddenSourcemapNoPluginsId = "const config = {\n entry: \"./src/index.js\",\n\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n\n devtool: \"hidden-cheap-source-map\",\n};\n\nmodule.exports = config;\n ";
89
+ var hiddenSourcemapNoPluginsIdResult = "const {\n sentryWebpackPlugin\n} = require(\"@sentry/webpack-plugin\");\n\nconst config = {\n entry: \"./src/index.js\",\n\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n\n devtool: \"hidden-source-map\",\n\n plugins: [sentryWebpackPlugin({\n authToken: process.env.SENTRY_AUTH_TOKEN,\n org: \"my-org\",\n project: \"my-project\"\n })]\n};\n\nmodule.exports = config;";
90
+ var arbitrarySourcemapNoPluginsId = "\nconst config = {\n entry: \"./src/index.js\",\n\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n\n devtool: getSourcemapSetting(),\n};\n\nmodule.exports = config;\n ";
91
+ var arbitrarySourcemapNoPluginsIdResult = "const {\n sentryWebpackPlugin\n} = require(\"@sentry/webpack-plugin\");\n\nconst config = {\n entry: \"./src/index.js\",\n\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n\n devtool: \"source-map\",\n\n plugins: [sentryWebpackPlugin({\n authToken: process.env.SENTRY_AUTH_TOKEN,\n org: \"my-org\",\n project: \"my-project\"\n })]\n};\n\nmodule.exports = config;";
92
+ var noSourcemapUndefinedPluginsPojo = "module.exports = {\n entry: \"./src/index.js\",\n plugins: undefined,\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n};";
93
+ var noSourcemapUndefinedPluginsPojoResult = "const {\n sentryWebpackPlugin\n} = require(\"@sentry/webpack-plugin\");\n\nmodule.exports = {\n entry: \"./src/index.js\",\n\n plugins: [sentryWebpackPlugin({\n authToken: process.env.SENTRY_AUTH_TOKEN,\n org: \"my-org\",\n project: \"my-project\"\n })],\n\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n\n devtool: \"source-map\"\n};";
94
+ var noSourcemapPluginsPojo = "module.exports = {\n entry: \"./src/index.js\",\n plugins: [\n new HtmlWebpackPlugin(),\n new MiniCssExtractPlugin(),\n ],\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n};";
95
+ var noSourcemapPluginsPojoResult = "const {\n sentryWebpackPlugin\n} = require(\"@sentry/webpack-plugin\");\n\nmodule.exports = {\n entry: \"./src/index.js\",\n\n plugins: [new HtmlWebpackPlugin(), new MiniCssExtractPlugin(), sentryWebpackPlugin({\n authToken: process.env.SENTRY_AUTH_TOKEN,\n org: \"my-org\",\n project: \"my-project\"\n })],\n\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n\n devtool: \"source-map\"\n};";
96
+ describe('modifyWebpackConfig', function () {
97
+ afterEach(function () {
98
+ fileContent = '';
99
+ jest.clearAllMocks();
100
+ });
101
+ it.each([
102
+ [
103
+ 'no sourcemap option, no plugins, object',
104
+ noSourcemapNoPluginsPojo,
105
+ noSourcemapNoPluginsPojoResult,
106
+ ],
107
+ [
108
+ 'no sourcemap option, no plugins, identifier',
109
+ noSourcemapsNoPluginsId,
110
+ noSourcemapsNoPluginsIdResult,
111
+ ],
112
+ [
113
+ 'hidden sourcemap option, no plugins, identifier',
114
+ hiddenSourcemapNoPluginsId,
115
+ hiddenSourcemapNoPluginsIdResult,
116
+ ],
117
+ [
118
+ 'arbitrary sourcemap option, no plugins, identifier',
119
+ arbitrarySourcemapNoPluginsId,
120
+ arbitrarySourcemapNoPluginsIdResult,
121
+ ],
122
+ [
123
+ 'no sourcemap option, plugins, object',
124
+ noSourcemapUndefinedPluginsPojo,
125
+ noSourcemapUndefinedPluginsPojoResult,
126
+ ],
127
+ [
128
+ 'no sourcemap option, plugins, object',
129
+ noSourcemapPluginsPojo,
130
+ noSourcemapPluginsPojoResult,
131
+ ],
132
+ ])('adds plugin and source maps emission to the webpack config (%s)', function (_, originalCode, expectedCode) { return __awaiter(void 0, void 0, void 0, function () {
133
+ var addedCode, _a, fileContent;
134
+ return __generator(this, function (_b) {
135
+ switch (_b.label) {
136
+ case 0:
137
+ updateFileContent(originalCode);
138
+ return [4 /*yield*/, (0, webpack_1.modifyWebpackConfig)('', {
139
+ authToken: '',
140
+ orgSlug: 'my-org',
141
+ projectSlug: 'my-project',
142
+ selfHosted: false,
143
+ url: 'https://sentry.io/',
144
+ })];
145
+ case 1:
146
+ addedCode = _b.sent();
147
+ expect(writeFileSpy).toHaveBeenCalledTimes(1);
148
+ _a = writeFileSpy.mock.calls[0], fileContent = _a[1];
149
+ expect(fileContent).toBe(expectedCode);
150
+ expect(addedCode).toBe(true);
151
+ return [2 /*return*/];
152
+ }
153
+ });
154
+ }); });
155
+ it('adds the url parameter to the webpack plugin options if self-hosted', function () { return __awaiter(void 0, void 0, void 0, function () {
156
+ var addedCode, _a, fileContent;
157
+ return __generator(this, function (_b) {
158
+ switch (_b.label) {
159
+ case 0:
160
+ updateFileContent(noSourcemapNoPluginsPojo);
161
+ return [4 /*yield*/, (0, webpack_1.modifyWebpackConfig)('', {
162
+ authToken: '',
163
+ orgSlug: 'my-org',
164
+ projectSlug: 'my-project',
165
+ selfHosted: true,
166
+ url: 'https://santry.io/',
167
+ })];
168
+ case 1:
169
+ addedCode = _b.sent();
170
+ expect(writeFileSpy).toHaveBeenCalledTimes(1);
171
+ _a = writeFileSpy.mock.calls[0], fileContent = _a[1];
172
+ expect(fileContent).toContain('url: "https://santry.io/"');
173
+ expect(addedCode).toBe(true);
174
+ return [2 /*return*/];
175
+ }
176
+ });
177
+ }); });
178
+ });
179
+ //# sourceMappingURL=webpack.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webpack.test.js","sourceRoot":"","sources":["../../../../test/sourcemaps/tools/webpack.test.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\n\nimport { modifyWebpackConfig } from '../../../src/sourcemaps/tools/webpack';\n\nfunction updateFileContent(content: string): void {\n fileContent = content;\n}\n\nlet fileContent = '';\n\njest.mock('@clack/prompts', () => {\n return {\n log: {\n info: jest.fn(),\n success: jest.fn(),\n },\n select: jest.fn().mockImplementation(() => Promise.resolve(true)),\n isCancel: jest.fn().mockReturnValue(false),\n };\n});\n\njest\n .spyOn(fs.promises, 'readFile')\n .mockImplementation(() => Promise.resolve(fileContent));\n\nconst writeFileSpy = jest\n .spyOn(fs.promises, 'writeFile')\n .mockImplementation(() => Promise.resolve(void 0));\n\nconst noSourcemapNoPluginsPojo = `module.exports = {\n entry: \"./src/index.js\",\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n};`;\n\nconst noSourcemapNoPluginsPojoResult = `const {\n sentryWebpackPlugin\n} = require(\"@sentry/webpack-plugin\");\n\nmodule.exports = {\n entry: \"./src/index.js\",\n\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n\n devtool: \"source-map\",\n\n plugins: [sentryWebpackPlugin({\n authToken: process.env.SENTRY_AUTH_TOKEN,\n org: \"my-org\",\n project: \"my-project\"\n })]\n};`;\n\nconst noSourcemapsNoPluginsId = `const config = {\n entry: \"./src/index.js\",\n\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n};\n\nmodule.exports = config;`;\n\nconst noSourcemapsNoPluginsIdResult = `const {\n sentryWebpackPlugin\n} = require(\"@sentry/webpack-plugin\");\n\nconst config = {\n entry: \"./src/index.js\",\n\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n\n devtool: \"source-map\",\n\n plugins: [sentryWebpackPlugin({\n authToken: process.env.SENTRY_AUTH_TOKEN,\n org: \"my-org\",\n project: \"my-project\"\n })]\n};\n\nmodule.exports = config;`;\n\nconst hiddenSourcemapNoPluginsId = `const config = {\n entry: \"./src/index.js\",\n\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n\n devtool: \"hidden-cheap-source-map\",\n};\n\nmodule.exports = config;\n `;\nconst hiddenSourcemapNoPluginsIdResult = `const {\n sentryWebpackPlugin\n} = require(\"@sentry/webpack-plugin\");\n\nconst config = {\n entry: \"./src/index.js\",\n\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n\n devtool: \"hidden-source-map\",\n\n plugins: [sentryWebpackPlugin({\n authToken: process.env.SENTRY_AUTH_TOKEN,\n org: \"my-org\",\n project: \"my-project\"\n })]\n};\n\nmodule.exports = config;`;\n\nconst arbitrarySourcemapNoPluginsId = `\nconst config = {\n entry: \"./src/index.js\",\n\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n\n devtool: getSourcemapSetting(),\n};\n\nmodule.exports = config;\n `;\nconst arbitrarySourcemapNoPluginsIdResult = `const {\n sentryWebpackPlugin\n} = require(\"@sentry/webpack-plugin\");\n\nconst config = {\n entry: \"./src/index.js\",\n\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n\n devtool: \"source-map\",\n\n plugins: [sentryWebpackPlugin({\n authToken: process.env.SENTRY_AUTH_TOKEN,\n org: \"my-org\",\n project: \"my-project\"\n })]\n};\n\nmodule.exports = config;`;\n\nconst noSourcemapUndefinedPluginsPojo = `module.exports = {\n entry: \"./src/index.js\",\n plugins: undefined,\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n};`;\n\nconst noSourcemapUndefinedPluginsPojoResult = `const {\n sentryWebpackPlugin\n} = require(\"@sentry/webpack-plugin\");\n\nmodule.exports = {\n entry: \"./src/index.js\",\n\n plugins: [sentryWebpackPlugin({\n authToken: process.env.SENTRY_AUTH_TOKEN,\n org: \"my-org\",\n project: \"my-project\"\n })],\n\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n\n devtool: \"source-map\"\n};`;\n\nconst noSourcemapPluginsPojo = `module.exports = {\n entry: \"./src/index.js\",\n plugins: [\n new HtmlWebpackPlugin(),\n new MiniCssExtractPlugin(),\n ],\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n};`;\n\nconst noSourcemapPluginsPojoResult = `const {\n sentryWebpackPlugin\n} = require(\"@sentry/webpack-plugin\");\n\nmodule.exports = {\n entry: \"./src/index.js\",\n\n plugins: [new HtmlWebpackPlugin(), new MiniCssExtractPlugin(), sentryWebpackPlugin({\n authToken: process.env.SENTRY_AUTH_TOKEN,\n org: \"my-org\",\n project: \"my-project\"\n })],\n\n output: {\n filename: \"main.js\",\n path: path.resolve(__dirname, \"build\"),\n },\n\n devtool: \"source-map\"\n};`;\n\ndescribe('modifyWebpackConfig', () => {\n afterEach(() => {\n fileContent = '';\n jest.clearAllMocks();\n });\n\n it.each([\n [\n 'no sourcemap option, no plugins, object',\n noSourcemapNoPluginsPojo,\n noSourcemapNoPluginsPojoResult,\n ],\n [\n 'no sourcemap option, no plugins, identifier',\n noSourcemapsNoPluginsId,\n noSourcemapsNoPluginsIdResult,\n ],\n [\n 'hidden sourcemap option, no plugins, identifier',\n hiddenSourcemapNoPluginsId,\n hiddenSourcemapNoPluginsIdResult,\n ],\n [\n 'arbitrary sourcemap option, no plugins, identifier',\n arbitrarySourcemapNoPluginsId,\n arbitrarySourcemapNoPluginsIdResult,\n ],\n [\n 'no sourcemap option, plugins, object',\n noSourcemapUndefinedPluginsPojo,\n noSourcemapUndefinedPluginsPojoResult,\n ],\n [\n 'no sourcemap option, plugins, object',\n noSourcemapPluginsPojo,\n noSourcemapPluginsPojoResult,\n ],\n ])(\n 'adds plugin and source maps emission to the webpack config (%s)',\n async (_, originalCode, expectedCode) => {\n updateFileContent(originalCode);\n\n // updateFileContent(originalCode);\n const addedCode = await modifyWebpackConfig('', {\n authToken: '',\n orgSlug: 'my-org',\n projectSlug: 'my-project',\n selfHosted: false,\n url: 'https://sentry.io/',\n });\n\n expect(writeFileSpy).toHaveBeenCalledTimes(1);\n const [[, fileContent]] = writeFileSpy.mock.calls;\n expect(fileContent).toBe(expectedCode);\n expect(addedCode).toBe(true);\n },\n );\n\n it('adds the url parameter to the webpack plugin options if self-hosted', async () => {\n updateFileContent(noSourcemapNoPluginsPojo);\n\n const addedCode = await modifyWebpackConfig('', {\n authToken: '',\n orgSlug: 'my-org',\n projectSlug: 'my-project',\n selfHosted: true,\n url: 'https://santry.io/',\n });\n\n expect(writeFileSpy).toHaveBeenCalledTimes(1);\n const [[, fileContent]] = writeFileSpy.mock.calls;\n expect(fileContent).toContain('url: \"https://santry.io/\"');\n expect(addedCode).toBe(true);\n });\n});\n"]}
@@ -1,20 +1,55 @@
1
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
+ };
2
25
  Object.defineProperty(exports, "__esModule", { value: true });
3
- //@ts-ignore
4
- var magicast_1 = require("magicast");
5
26
  var ast_utils_1 = require("../../src/utils/ast-utils");
27
+ var recast = __importStar(require("recast"));
6
28
  describe('AST utils', function () {
7
29
  describe('hasSentryContent', function () {
8
- it("returns true if a '@sentry/' import was found in the parsed module", function () {
9
- var code = "\n import { sentryVitePlugin } from \"@sentry/vite-plugin\";\n import * as somethingelse from 'gs';\n\n export default {\n plugins: [sentryVitePlugin()]\n }\n ";
10
- expect((0, ast_utils_1.hasSentryContent)((0, magicast_1.parseModule)(code))).toBe(true);
30
+ it.each([
31
+ "\n const { sentryVitePlugin } = require(\"@sentry/vite-plugin\");\n const somethingelse = require('gs');\n ",
32
+ "\n import { sentryVitePlugin } from \"@sentry/vite-plugin\";\n import * as somethingelse from 'gs';\n\n export default {\n plugins: [sentryVitePlugin()]\n }\n ",
33
+ ])("returns true if a require('@sentry/') call was found in the parsed module", function (code) {
34
+ // recast.parse returns a Program node (or fails) but it's badly typed as any
35
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
36
+ var program = recast.parse(code)
37
+ .program;
38
+ expect((0, ast_utils_1.hasSentryContent)(program)).toBe(true);
11
39
  });
12
40
  it.each([
41
+ "const whatever = require('something')",
42
+ "// const {sentryWebpackPlugin} = require('@sentry/webpack-plugin')",
43
+ "const {sAntryWebpackPlugin} = require('webpack-plugin-@sentry')",
13
44
  "\n import * as somethingelse from 'gs';\n export default {\n plugins: []\n }\n ",
14
45
  "import * as somethingelse from 'gs';\n // import { sentryVitePlugin } from \"@sentry/vite-plugin\"\n export default {\n plugins: []\n }\n ",
15
46
  "import * as thirdPartyVitePlugin from \"vite-plugin-@sentry\"\n export default {\n plugins: [thirdPartyVitePlugin()]\n }\n ",
16
- ])("reutrns false for modules without a valid '@sentry/' import", function (code) {
17
- expect((0, ast_utils_1.hasSentryContent)((0, magicast_1.parseModule)(code))).toBe(false);
47
+ ])("returns false if the file doesn't contain any require('@sentry/') calls", function (code) {
48
+ // recast.parse returns a Program node (or fails) but it's badly typed as any
49
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
50
+ var program = recast.parse(code)
51
+ .program;
52
+ expect((0, ast_utils_1.hasSentryContent)(program)).toBe(false);
18
53
  });
19
54
  });
20
55
  });
@@ -1 +1 @@
1
- {"version":3,"file":"ast-utils.test.js","sourceRoot":"","sources":["../../../test/utils/ast-utils.test.ts"],"names":[],"mappings":";;AAAA,YAAY;AACZ,qCAAuC;AACvC,uDAA6D;AAE7D,QAAQ,CAAC,WAAW,EAAE;IACpB,QAAQ,CAAC,kBAAkB,EAAE;QAC3B,EAAE,CAAC,oEAAoE,EAAE;YACvE,IAAM,IAAI,GAAG,6MAOZ,CAAC;YAEF,MAAM,CAAC,IAAA,4BAAgB,EAAC,IAAA,sBAAW,EAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;QACH,EAAE,CAAC,IAAI,CAAC;YACN,8GAKC;YACD,0KAKC;YACD,mJAIC;SACF,CAAC,CACA,6DAA6D,EAC7D,UAAC,IAAI;YACH,MAAM,CAAC,IAAA,4BAAgB,EAAC,IAAA,sBAAW,EAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1D,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["//@ts-ignore\nimport { parseModule } from 'magicast';\nimport { hasSentryContent } from '../../src/utils/ast-utils';\n\ndescribe('AST utils', () => {\n describe('hasSentryContent', () => {\n it(\"returns true if a '@sentry/' import was found in the parsed module\", () => {\n const code = `\n import { sentryVitePlugin } from \"@sentry/vite-plugin\";\n import * as somethingelse from 'gs';\n\n export default {\n plugins: [sentryVitePlugin()]\n }\n `;\n\n expect(hasSentryContent(parseModule(code))).toBe(true);\n });\n it.each([\n `\n import * as somethingelse from 'gs';\n export default {\n plugins: []\n }\n `,\n `import * as somethingelse from 'gs';\n // import { sentryVitePlugin } from \"@sentry/vite-plugin\"\n export default {\n plugins: []\n }\n `,\n `import * as thirdPartyVitePlugin from \"vite-plugin-@sentry\"\n export default {\n plugins: [thirdPartyVitePlugin()]\n }\n `,\n ])(\n \"reutrns false for modules without a valid '@sentry/' import\",\n (code) => {\n expect(hasSentryContent(parseModule(code))).toBe(false);\n },\n );\n });\n});\n"]}
1
+ {"version":3,"file":"ast-utils.test.js","sourceRoot":"","sources":["../../../test/utils/ast-utils.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uDAA6D;AAE7D,6CAAiC;AAEjC,QAAQ,CAAC,WAAW,EAAE;IACpB,QAAQ,CAAC,kBAAkB,EAAE;QAC3B,EAAE,CAAC,IAAI,CAAC;YACN,0HAGD;YACC,6MAOC;SACF,CAAC,CACA,2EAA2E,EAC3E,UAAC,IAAI;YACH,6EAA6E;YAC7E,sEAAsE;YACtE,IAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;iBAC/B,OAA0C,CAAC;YAC9C,MAAM,CAAC,IAAA,4BAAgB,EAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C,CAAC,CACF,CAAC;QAEF,EAAE,CAAC,IAAI,CAAC;YACN,uCAAuC;YACvC,oEAAoE;YACpE,iEAAiE;YACjE,8GAKC;YACD,0KAKC;YACD,mJAIC;SACF,CAAC,CACA,yEAAyE,EACzE,UAAC,IAAI;YACH,6EAA6E;YAC7E,sEAAsE;YACtE,IAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;iBAC/B,OAA0C,CAAC;YAC9C,MAAM,CAAC,IAAA,4BAAgB,EAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChD,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { hasSentryContent } from '../../src/utils/ast-utils';\n\nimport * as recast from 'recast';\n\ndescribe('AST utils', () => {\n describe('hasSentryContent', () => {\n it.each([\n `\n const { sentryVitePlugin } = require(\"@sentry/vite-plugin\");\n const somethingelse = require('gs');\n `,\n `\n import { sentryVitePlugin } from \"@sentry/vite-plugin\";\n import * as somethingelse from 'gs';\n\n export default {\n plugins: [sentryVitePlugin()]\n }\n `,\n ])(\n \"returns true if a require('@sentry/') call was found in the parsed module\",\n (code) => {\n // recast.parse returns a Program node (or fails) but it's badly typed as any\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const program = recast.parse(code)\n .program as recast.types.namedTypes.Program;\n expect(hasSentryContent(program)).toBe(true);\n },\n );\n\n it.each([\n `const whatever = require('something')`,\n `// const {sentryWebpackPlugin} = require('@sentry/webpack-plugin')`,\n `const {sAntryWebpackPlugin} = require('webpack-plugin-@sentry')`,\n `\n import * as somethingelse from 'gs';\n export default {\n plugins: []\n }\n `,\n `import * as somethingelse from 'gs';\n // import { sentryVitePlugin } from \"@sentry/vite-plugin\"\n export default {\n plugins: []\n }\n `,\n `import * as thirdPartyVitePlugin from \"vite-plugin-@sentry\"\n export default {\n plugins: [thirdPartyVitePlugin()]\n }\n `,\n ])(\n \"returns false if the file doesn't contain any require('@sentry/') calls\",\n (code) => {\n // recast.parse returns a Program node (or fails) but it's badly typed as any\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const program = recast.parse(code)\n .program as recast.types.namedTypes.Program;\n expect(hasSentryContent(program)).toBe(false);\n },\n );\n });\n});\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/wizard",
3
- "version": "3.11.0",
3
+ "version": "3.12.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
  }
@@ -100,6 +102,7 @@ async function runAndroidWizardWithTelemetry(
100
102
  "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
103
  );
102
104
  }
105
+ Sentry.setTag('gradle-plugin-added', pluginAdded);
103
106
 
104
107
  // ======== STEP 2. Configure Sentry SDK via AndroidManifest ============
105
108
  clack.log.step(
@@ -119,6 +122,7 @@ async function runAndroidWizardWithTelemetry(
119
122
  "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
123
  );
121
124
  }
125
+ Sentry.setTag('android-manifest-updated', manifestUpdated);
122
126
 
123
127
  // ======== STEP 3. Patch Main Activity with a test error snippet ============
124
128
  clack.log.step(
@@ -133,10 +137,13 @@ async function runAndroidWizardWithTelemetry(
133
137
  packageName = gradle.getNamespace(appFile);
134
138
  }
135
139
  const activityName = mainActivity.activityName;
140
+ Sentry.setTag('has-activity-name', !!activityName);
141
+ Sentry.setTag('has-package-name', !!packageName);
136
142
  if (!activityName || !packageName) {
137
143
  clack.log.warn(
138
144
  "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
145
  );
146
+ Sentry.captureException('Could not find Main Activity');
140
147
  } else {
141
148
  const packageNameStable = packageName;
142
149
  const activityFile = traceStep('Find Main Activity Source File', () =>
@@ -151,6 +158,7 @@ async function runAndroidWizardWithTelemetry(
151
158
  "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
159
  );
153
160
  }
161
+ Sentry.setTag('main-activity-patched', activityPatched);
154
162
  }
155
163
 
156
164
  // ======== STEP 4. Add sentry-cli config file ============
@@ -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;