UNPKG

@sentry/wizard

Version:

Sentry wizard helping you to configure your project

365 lines 19.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { 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; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.modifyWebpackConfig = exports.configureWebPackPlugin = void 0; var path = __importStar(require("path")); var fs = __importStar(require("fs")); // @ts-ignore - clack is ESM and TS complains about that. It works though var clack = __importStar(require("@clack/prompts")); var chalk_1 = __importDefault(require("chalk")); var recast = __importStar(require("recast")); var Sentry = __importStar(require("@sentry/node")); var clack_utils_1 = require("../../utils/clack-utils"); var package_json_1 = require("../../utils/package-json"); var ast_utils_1 = require("../../utils/ast-utils"); var debug_1 = require("../../utils/debug"); var getCodeSnippet = function (options, colors) { return (0, clack_utils_1.makeCodeSnippet)(colors, function (unchanged, plus) { return unchanged("".concat(plus('const { sentryWebpackPlugin } = require("@sentry/webpack-plugin");'), "\n\nmodule.exports = {\n // ... other options\n ").concat(plus('devtool: "source-map", // Source map generation must be turned on'), "\n plugins: [\n // Put the Sentry Webpack plugin after all other plugins\n ").concat(plus("sentryWebpackPlugin({\n authToken: process.env.SENTRY_AUTH_TOKEN,\n org: \"".concat(options.orgSlug, "\",\n project: \"").concat(options.projectSlug, "\",").concat(options.selfHosted ? "\n url: \"".concat(options.url, "\",") : '', "\n }),")), "\n ],\n}")); }); }; var configureWebPackPlugin = function (options) { return __awaiter(void 0, void 0, void 0, function () { var _a, _b, _c, webpackConfigPath, _d, successfullyAdded; var _e; var _f; return __generator(this, function (_g) { switch (_g.label) { case 0: _a = clack_utils_1.installPackage; _e = { packageName: '@sentry/webpack-plugin' }; _b = package_json_1.hasPackageInstalled; _c = ['@sentry/webpack-plugin']; return [4 /*yield*/, (0, clack_utils_1.getPackageDotJson)()]; case 1: return [4 /*yield*/, _a.apply(void 0, [(_e.alreadyInstalled = _b.apply(void 0, _c.concat([_g.sent()])), _e)])]; case 2: _g.sent(); if (!((_f = (0, ast_utils_1.findFile)(path.resolve(process.cwd(), 'webpack.config'))) !== null && _f !== void 0)) return [3 /*break*/, 3]; _d = _f; return [3 /*break*/, 5]; case 3: return [4 /*yield*/, (0, clack_utils_1.askForToolConfigPath)('Webpack', 'webpack.config.js')]; case 4: _d = (_g.sent()); _g.label = 5; case 5: webpackConfigPath = _d; successfullyAdded = false; if (!webpackConfigPath) return [3 /*break*/, 7]; return [4 /*yield*/, modifyWebpackConfig(webpackConfigPath, options)]; case 6: successfullyAdded = _g.sent(); return [3 /*break*/, 9]; case 7: return [4 /*yield*/, (0, clack_utils_1.createNewConfigFile)(path.join(process.cwd(), 'webpack.config.js'), getCodeSnippet(options, false), 'More information about Webpack configs: https://vitejs.dev/config/')]; case 8: successfullyAdded = _g.sent(); Sentry.setTag('created-new-config', successfullyAdded ? 'success' : 'fail'); _g.label = 9; case 9: if (!successfullyAdded) return [3 /*break*/, 10]; clack.log.info("We recommend checking the ".concat(webpackConfigPath ? 'modified' : 'added', " file after the wizard finished to ensure it works with your build setup.")); Sentry.setTag('ast-mod', 'success'); return [3 /*break*/, 12]; case 10: Sentry.setTag('ast-mod', 'fail'); return [4 /*yield*/, (0, clack_utils_1.showCopyPasteInstructions)(path.basename(webpackConfigPath || 'webpack.config.js'), getCodeSnippet(options, true))]; case 11: _g.sent(); _g.label = 12; case 12: return [4 /*yield*/, (0, clack_utils_1.addDotEnvSentryBuildPluginFile)(options.authToken)]; case 13: _g.sent(); return [2 /*return*/]; } }); }); }; exports.configureWebPackPlugin = configureWebPackPlugin; /** * Modifies a webpack config file to enable source map generation and add the Sentry webpack plugin * exported only for testing */ function modifyWebpackConfig(webpackConfigPath, options) { return __awaiter(this, void 0, void 0, function () { var webpackConfig, prettyConfigFilename, program, exportStmt, configObject, enabledSourcemaps, addedPlugin, code, e_1; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 4, , 5]); return [4 /*yield*/, fs.promises.readFile(webpackConfigPath, { encoding: 'utf-8', })]; case 1: webpackConfig = _a.sent(); prettyConfigFilename = chalk_1.default.cyan(path.basename(webpackConfigPath)); program = recast.parse(webpackConfig.toString()).program; return [4 /*yield*/, shouldModifyWebpackConfig(program, prettyConfigFilename)]; case 2: if (!(_a.sent())) { // Sentry tag is set in shouldModifyWebpackConfig return [2 /*return*/, false]; } exportStmt = getCjsModuleExports(program); if (!exportStmt) { // We only care about CJS at the moment since it's probably the most widely used format for webpack configs. (0, debug_1.debug)("Could not find module.exports = {...} in ".concat(webpackConfigPath, ".")); Sentry.setTag('ast-mod-fail-reason', 'config-object-not-found'); return [2 /*return*/, false]; } configObject = getWebpackConfigObject(exportStmt, program); if (!configObject) { (0, debug_1.debug)("Couldn't find config object in ".concat(webpackConfigPath)); Sentry.setTag('ast-mod-fail-reason', 'config-object-not-found'); return [2 /*return*/, false]; } enabledSourcemaps = enableSourcemapsGeneration(configObject); if (enabledSourcemaps) { clack.log.success("Enabled source map generation in ".concat(prettyConfigFilename, ".")); } else { clack.log.warn("Couldn't enable source maps generation in ".concat(prettyConfigFilename, " Please follow the instructions below.")); Sentry.setTag('ast-mod-fail-reason', 'insertion-fail'); return [2 /*return*/, false]; } addedPlugin = addSentryWebpackPlugin(program, configObject, options); if (addedPlugin) { clack.log.success("Added Sentry webpack plugin to ".concat(prettyConfigFilename, ".")); } else { clack.log.warn("Couldn't add Sentry webpack plugin to ".concat(prettyConfigFilename, ". Please follow the instructions below.")); Sentry.setTag('ast-mod-fail-reason', 'insertion-fail'); return [2 /*return*/, false]; } code = recast.print(program).code; return [4 /*yield*/, fs.promises.writeFile(webpackConfigPath, code)]; case 3: _a.sent(); return [2 /*return*/, true]; case 4: e_1 = _a.sent(); Sentry.setTag('ast-mod-fail-reason', 'insertion-fail'); (0, debug_1.debug)(e_1); return [2 /*return*/, false]; case 5: return [2 /*return*/]; } }); }); } exports.modifyWebpackConfig = modifyWebpackConfig; function shouldModifyWebpackConfig(program, prettyConfigFilename) { return __awaiter(this, void 0, void 0, function () { var shouldContinue; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!(0, ast_utils_1.hasSentryContent)(program)) return [3 /*break*/, 2]; return [4 /*yield*/, (0, clack_utils_1.abortIfCancelled)(clack.select({ message: "Seems like ".concat(prettyConfigFilename, " already contains Sentry-related code. Should the wizard modify it anyway?"), options: [ { label: 'Yes, add the Sentry Webpack plugin', value: true, }, { label: 'No, show me instructions to manually add the plugin', value: false, }, ], initialValue: true, }))]; case 1: shouldContinue = _a.sent(); if (!shouldContinue) { Sentry.setTag('ast-mod-fail-reason', 'has-sentry-content'); return [2 /*return*/, false]; } _a.label = 2; case 2: return [2 /*return*/, true]; } }); }); } function addSentryWebpackPlugin(program, configObj, options) { var b = addSentryWebpackPluginImport(program); var sentryPluginCall = b.callExpression(b.identifier('sentryWebpackPlugin'), [ b.objectExpression(__spreadArray([ b.objectProperty(b.identifier('authToken'), b.identifier('process.env.SENTRY_AUTH_TOKEN')), b.objectProperty(b.identifier('org'), b.stringLiteral(options.orgSlug)), b.objectProperty(b.identifier('project'), b.stringLiteral(options.projectSlug)) ], (options.selfHosted ? [ b.objectProperty(b.identifier('url'), b.stringLiteral(options.url)), ] : []), true)), ]); var pluginsProp = configObj.properties.find(function (p) { return p.type === 'Property' && p.key.type === 'Identifier' && p.key.name === 'plugins'; }); if (pluginsProp) { if (pluginsProp.value.type === 'ArrayExpression') { pluginsProp.value.elements.push(sentryPluginCall); } else { pluginsProp.value = b.arrayExpression([sentryPluginCall]); } return true; } configObj.properties.push(b.objectProperty(b.identifier('plugins'), b.arrayExpression([sentryPluginCall]))); return true; } function addSentryWebpackPluginImport(program) { var b = recast.types.builders; var sentryPluginRequireStmt = b.variableDeclaration('const', [ b.variableDeclarator(b.objectPattern([ b.objectProperty.from({ key: b.identifier('sentryWebpackPlugin'), value: b.identifier('sentryWebpackPlugin'), shorthand: true, }), ]), b.callExpression(b.identifier('require'), [ b.stringLiteral('@sentry/webpack-plugin'), ])), ]); program.body.unshift(sentryPluginRequireStmt); return b; } function enableSourcemapsGeneration(configObj) { var _a; var b = recast.types.builders; var devtoolProp = configObj.properties.find(function (p) { return p.type === 'Property' && p.key.type === 'Identifier' && p.key.name === 'devtool'; }); if (devtoolProp) { // devtool can have quite a lot of source maps values. // see: https://webpack.js.org/configuration/devtool/#devtool // For Sentry to work best, we should set it to "source-map" or "hidden-source-map" // Heuristic: // - all values that contain "hidden" will be set to "hidden-source-map" // - all other values will be set to "source-map" if ((devtoolProp.value.type === 'Literal' || devtoolProp.value.type === 'StringLiteral') && ((_a = devtoolProp.value.value) === null || _a === void 0 ? void 0 : _a.toString().startsWith('hidden-'))) { devtoolProp.value = b.stringLiteral('hidden-source-map'); } else { devtoolProp.value = b.stringLiteral('source-map'); } return true; } configObj.properties.push(b.objectProperty(b.identifier('devtool'), b.stringLiteral('source-map'))); return true; } function getWebpackConfigObject(moduleExports, program) { var _a; var rhs = moduleExports.right; if (rhs.type === 'ObjectExpression') { return rhs; } if (rhs.type === 'Identifier') { var configId_1 = rhs.name; var configDeclaration = program.body.find(function (s) { return s.type === 'VariableDeclaration' && !!s.declarations.find(function (d) { return d.type === 'VariableDeclarator' && d.id.type === 'Identifier' && d.id.name === configId_1; }); }); var declarator = configDeclaration === null || configDeclaration === void 0 ? void 0 : configDeclaration.declarations.find(function (d) { return d.type === 'VariableDeclarator' && d.id.type === 'Identifier' && d.id.name === configId_1; }); return ((_a = declarator === null || declarator === void 0 ? void 0 : declarator.init) === null || _a === void 0 ? void 0 : _a.type) === 'ObjectExpression' ? declarator.init : undefined; } return undefined; } function getCjsModuleExports(program) { var moduleExports = program.body.find(function (s) { return s.type === 'ExpressionStatement' && s.expression.type === 'AssignmentExpression' && s.expression.left.type === 'MemberExpression' && s.expression.left.object.type === 'Identifier' && s.expression.left.object.name === 'module' && s.expression.left.property.type === 'Identifier' && s.expression.left.property.name === 'exports'; }); return moduleExports === null || moduleExports === void 0 ? void 0 : moduleExports.expression; } //# sourceMappingURL=webpack.js.map