@sentry/wizard
Version:
Sentry wizard helping you to configure your project
225 lines • 10.6 kB
JavaScript
;
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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDevDependenciesLocation = exports.getDependenciesLocation = exports.getLastImportLineLocation = exports.patchMainContent = exports.patchMain = exports.addProperties = exports.patchPubspec = exports.findFile = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const Sentry = __importStar(require("@sentry/node"));
// @ts-expect-error - clack is ESM and TS complains about that. It works though
const clack = __importStar(require("@clack/prompts"));
const chalk_1 = __importDefault(require("chalk"));
const templates_1 = require("./templates");
const clack_1 = require("../utils/clack");
/**
* Recursively finds a file per name in subfolders.
* @param dir - The directory to start searching.
* @param name - The name of the file including path extension.
* @returns The path to the main.dart file or null if not found.
*/
function findFile(dir, name) {
const files = fs.readdirSync(dir);
for (const file of files) {
const fullPath = path.join(dir, file);
const stats = fs.statSync(fullPath);
if (stats.isDirectory()) {
const result = findFile(fullPath, name);
if (result) {
return result;
}
}
else if (file === name) {
return fullPath;
}
}
return null;
}
exports.findFile = findFile;
function patchPubspec(pubspecFile, sentryDartFlutterVersion, sentryDartPluginVersion, project, org) {
try {
if (!pubspecFile) {
throw new Error('pubspec.yaml is not provided or invalid.');
}
let pubspecContent = fs.readFileSync(pubspecFile, 'utf8');
if (!pubspecContent.includes('sentry_flutter:')) {
const dependenciesIndex = getDependenciesLocation(pubspecContent);
pubspecContent =
pubspecContent.slice(0, dependenciesIndex) +
` sentry_flutter: ${sentryDartFlutterVersion}\n` +
pubspecContent.slice(dependenciesIndex);
clack.log.success(chalk_1.default.greenBright(`${chalk_1.default.bold('sentry_flutter')} added to pubspec.yaml`));
}
else {
clack.log.success(chalk_1.default.greenBright(`${chalk_1.default.bold('sentry_flutter')} is already included in pubspec.yaml`));
}
if (!pubspecContent.includes('sentry_dart_plugin:')) {
const devDependenciesIndex = getDevDependenciesLocation(pubspecContent);
pubspecContent =
pubspecContent.slice(0, devDependenciesIndex) +
` sentry_dart_plugin: ${sentryDartPluginVersion}\n` +
pubspecContent.slice(devDependenciesIndex);
clack.log.success(chalk_1.default.greenBright(`${chalk_1.default.bold('sentry_dart_plugin')} added to pubspec.yaml`));
}
else {
clack.log.success(chalk_1.default.greenBright(`${chalk_1.default.bold('sentry_dart_plugin')} is already included in pubspec.yaml`));
}
if (!pubspecContent.includes('sentry:')) {
pubspecContent += '\n';
pubspecContent += (0, templates_1.pubspecOptions)(project, org);
clack.log.success(chalk_1.default.greenBright(`${chalk_1.default.bold('sentry plugin configuration')} added to pubspec.yaml`));
}
else {
clack.log.success(chalk_1.default.greenBright(`${chalk_1.default.bold('sentry plugin configuration')} is already included in pubspec.yaml`));
}
fs.writeFileSync(pubspecFile, pubspecContent, 'utf8');
return true;
}
catch (error) {
clack.log.warn(`Failed to read/write ${chalk_1.default.cyan('pubspec.yaml')} file.`);
Sentry.captureException(error);
return false;
}
}
exports.patchPubspec = patchPubspec;
function addProperties(pubspecFile, authToken) {
try {
if (!pubspecFile) {
throw new Error('pubspec.yaml is not provided or invalid.');
}
const pubspecDir = path.dirname(pubspecFile);
const sentryPropertiesFileName = 'sentry.properties';
const sentryPropertiesFile = path.join(pubspecDir, sentryPropertiesFileName);
const sentryPropertiesContent = (0, templates_1.sentryProperties)(authToken);
fs.writeFileSync(sentryPropertiesFile, sentryPropertiesContent, 'utf8');
const gitignoreFile = path.join(pubspecDir, '.gitignore');
if (fs.existsSync(gitignoreFile)) {
fs.appendFileSync(gitignoreFile, `\n${sentryPropertiesFileName}\n`);
}
else {
fs.writeFileSync(gitignoreFile, `${sentryPropertiesFileName}\n`, 'utf8');
}
return true;
}
catch (error) {
clack.log.warn(`Failed to read/write ${chalk_1.default.cyan('pubspec.yaml')} file.`);
Sentry.captureException(error);
return false;
}
}
exports.addProperties = addProperties;
async function patchMain(mainFile, dsn, canEnableProfiling) {
try {
if (!mainFile) {
throw new Error('pubspec.yaml is not provided or invalid.');
}
let mainContent = fs.readFileSync(mainFile, 'utf8');
if (/import\s+['"]package[:]sentry_flutter\/sentry_flutter\.dart['"];?/i.test(mainContent)) {
// sentry is already configured
clack.log.success(chalk_1.default.greenBright(`${chalk_1.default.bold('main.dart')} already has Sentry configured.`));
return true;
}
const features = [
{
id: 'tracing',
prompt: `Do you want to enable ${chalk_1.default.bold('Tracing')} to track the performance of your application?`,
enabledHint: 'recommended',
},
];
if (canEnableProfiling) {
features.push({
id: 'profiling',
prompt: `Do you want to enable ${chalk_1.default.bold('Profiling')} to analyze CPU usage and optimize performance-critical code on iOS & macOS?`,
enabledHint: 'recommended, tracing must be enabled',
});
}
features.push({
id: 'replay',
prompt: `Do you want to enable ${chalk_1.default.bold('Session Replay')} to record user interactions and debug issues?`,
enabledHint: 'recommended',
});
const selectedFeatures = await (0, clack_1.featureSelectionPrompt)(features);
const normalizedSelectedFeatures = {
tracing: selectedFeatures.tracing ?? false,
profiling: selectedFeatures.profiling ?? false,
replay: selectedFeatures.replay ?? false,
};
mainContent = patchMainContent(dsn, mainContent, normalizedSelectedFeatures);
if (normalizedSelectedFeatures.replay) {
clack.log.info(`Session Replay will be enabled with default settings (replaysSessionSampleRate: ${templates_1.sessionReplaySampleRate}, replaysOnErrorSampleRate: ${templates_1.sessionReplayOnErrorSampleRate}).`);
clack.log.message('By default, all text content, images, and webviews will be masked for privacy. You can customize this in your code later.');
}
fs.writeFileSync(mainFile, mainContent, 'utf8');
clack.log.success(chalk_1.default.greenBright(`Patched ${chalk_1.default.bold('main.dart')} with the Sentry setup and test error snippet.`));
return true;
}
catch (error) {
clack.log.warn(`Failed to read/write ${chalk_1.default.cyan('main.dart')} file.`);
Sentry.captureException(error);
return false;
}
}
exports.patchMain = patchMain;
function patchMainContent(dsn, mainContent, selectedFeatures) {
const importIndex = getLastImportLineLocation(mainContent);
mainContent =
mainContent.slice(0, importIndex) +
templates_1.sentryImport +
mainContent.slice(importIndex);
// Find and replace `runApp(...)`
mainContent = mainContent.replace(/runApp\(([\s\S]*?)\);/g, // Match the `runApp(...)` invocation
(_, runAppArgs) => (0, templates_1.initSnippet)(dsn, selectedFeatures, runAppArgs));
// Make the `main` function async if it's not already
mainContent = mainContent.replace(/void\s+main\(\)\s*\{/g, 'Future<void> main() async {');
return mainContent;
}
exports.patchMainContent = patchMainContent;
function getLastImportLineLocation(sourceCode) {
const importRegex = /import\s+['"].*['"].*;/gim;
return getLastReqExpLocation(sourceCode, importRegex);
}
exports.getLastImportLineLocation = getLastImportLineLocation;
function getDependenciesLocation(sourceCode) {
const dependencyRegex = /^dependencies:\s*$/gim;
return getLastReqExpLocation(sourceCode, dependencyRegex);
}
exports.getDependenciesLocation = getDependenciesLocation;
function getDevDependenciesLocation(sourceCode) {
const dependencyRegex = /^dev_dependencies:\s*$/gim;
return getLastReqExpLocation(sourceCode, dependencyRegex);
}
exports.getDevDependenciesLocation = getDevDependenciesLocation;
// Helper
function getLastReqExpLocation(sourceCode, regExp) {
let match = regExp.exec(sourceCode);
let importIndex = 0;
while (match) {
importIndex = match.index + match[0].length + 1;
match = regExp.exec(sourceCode);
}
return importIndex;
}
//# sourceMappingURL=code-tools.js.map