@sentry/wizard
Version:
Sentry wizard helping you to configure your project
218 lines • 8.2 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;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getLastRequireIndex = exports.printJsonC = exports.parseJsonC = exports.setOrUpdateObjectProperty = exports.getOrSetObjectProperty = exports.getObjectProperty = exports.hasSentryContent = exports.findFile = void 0;
const fs = __importStar(require("fs"));
const recast = __importStar(require("recast"));
const b = recast.types.builders;
/**
* Checks if a file where we don't know its concrete file type yet exists
* and returns the full path to the file with the correct file type.
*/
function findFile(filePath, fileTypes = ['.js', '.ts', '.mjs', '.cjs']) {
return fileTypes
.map((type) => `${filePath}${type}`)
.find((file) => fs.existsSync(file));
}
exports.findFile = findFile;
/**
* checks for require('@sentry/*') syntax
*/
function hasSentryContent(program) {
let foundSentry = false;
recast.visit(program, {
visitStringLiteral(path) {
foundSentry = foundSentry || path.node.value.startsWith('@sentry/');
this.traverse(path);
},
visitLiteral(path) {
foundSentry =
foundSentry || path.node.value?.toString().startsWith('@sentry/');
this.traverse(path);
},
});
return !!foundSentry;
}
exports.hasSentryContent = hasSentryContent;
/**
* Searches for a property of an ObjectExpression by name
*
* @param object the ObjectExpression to search in
* @param name the name of the property to search for
*
* @returns the property if it exists
*/
function getObjectProperty(object, name) {
return object.properties.find((p) => {
const isObjectProp = p.type === 'Property' || p.type === 'ObjectProperty';
if (!isObjectProp) {
return false;
}
const hasMatchingLiteralKey = isObjectProp &&
(p.key.type === 'Literal' || p.key.type === 'StringLiteral') &&
p.key.value === name;
if (hasMatchingLiteralKey) {
return true;
}
// has matching identifier key
return isObjectProp && p.key.type === 'Identifier' && p.key.name === name;
});
}
exports.getObjectProperty = getObjectProperty;
/**
* Attempts to find a property of an ObjectExpression by name. If it doesn't exist,
* the property will be added to the ObjectExpression with the provided default value.
*
* @param object the parent object expression to search in
* @param name the name of the property to search for
* @param defaultValue the default value to set if the property doesn't exist
*
* @returns the
*/
function getOrSetObjectProperty(object, name, defaultValue) {
const existingProperty = getObjectProperty(object, name);
if (existingProperty) {
return existingProperty;
}
const newProperty = b.property.from({
kind: 'init',
key: b.stringLiteral(name),
value: defaultValue,
});
object.properties.push(newProperty);
return newProperty;
}
exports.getOrSetObjectProperty = getOrSetObjectProperty;
/**
* Sets a property of an ObjectExpression if it exists, otherwise adds it
* to the ObjectExpression. Optionally, a comment can be added to the
* property.
*
* @param object the ObjectExpression to set the property on
* @param name the name of the property to set
* @param value the value of the property to set
* @param comment (optional) a comment to add to the property
*/
function setOrUpdateObjectProperty(object, name, value, comment) {
const newComments = comment &&
comment.split('\n').map((c) => b.commentLine(` ${c}`, true, false));
const existingProperty = getObjectProperty(object, name);
if (existingProperty) {
existingProperty.value = value;
if (newComments) {
existingProperty.comments = [
...(existingProperty?.comments || []),
...newComments,
];
}
}
else {
object.properties.push(b.objectProperty.from({
key: b.stringLiteral(name),
value,
...(newComments && {
comments: newComments,
}),
}));
}
}
exports.setOrUpdateObjectProperty = setOrUpdateObjectProperty;
/**
* Parses a JSON string with (potential) comments (JSON-C) and returns the JS AST
* that can be walked and modified with recast like a normal JS AST.
*
* This is done by wrapping the JSON-C string in parentheses, thereby making it
* a JS `Program` with an `ExpressionStatement` as its body. The expression is then
* extracted from the AST and returned alongside the AST.
*
* To preserve as much original formatting as possible, the returned `ast`
* property should be passed to {@link `printJsonC`} to get the JSON-C string back.
*
* If the input is not valid JSON-C, the result will be undefined.
*
* @see {@link JsonCParseResult}
*
* @param jsonString a JSON-C string
*
* @returns a {@link JsonCParseResult}, containing either the JSON-C object and the AST or undefined in both cases
*/
function parseJsonC(jsonString) {
try {
const jsTsConfig = `(${jsonString})`;
// no idea why recast returns any here, this is dumb :/
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const ast = recast.parse(jsTsConfig.toString()).program;
const jsonObject = (ast.body[0].type === 'ExpressionStatement' &&
ast.body[0].expression.type === 'ObjectExpression' &&
ast.body[0].expression) ||
undefined;
if (jsonObject) {
return { jsonObject, ast };
}
}
catch {
/* empty */
}
return { jsonObject: undefined, ast: undefined };
}
exports.parseJsonC = parseJsonC;
/**
* Takes the AST of a parsed JSON-C "program" and returns the JSON-C string without
* any of the temporary JS wrapper code that was previously applied.
*
* Only use this in conjunction with {@link `parseJsonC`}
*
* @param ast the `ast` returned from {@link `parseJsonC`}
*
* @returns the JSON-C string
*/
function printJsonC(ast) {
const js = recast.print(ast).code;
return js.substring(1, js.length - 1);
}
exports.printJsonC = printJsonC;
/**
* Walks the program body and returns index of the last variable assignment initialized by require statement.
* Only counts top level require statements.
*
* @returns index of the last `const foo = require('bar');` statement or -1 if none was found.
*/
function getLastRequireIndex(program) {
let lastRequireIdex = -1;
program.body.forEach((s, i) => {
if (s.type === 'VariableDeclaration' &&
s.declarations[0].type === 'VariableDeclarator' &&
s.declarations[0].init !== null &&
typeof s.declarations[0].init !== 'undefined' &&
s.declarations[0].init.type === 'CallExpression' &&
s.declarations[0].init.callee.type === 'Identifier' &&
s.declarations[0].init.callee.name === 'require') {
lastRequireIdex = i;
}
});
return lastRequireIdex;
}
exports.getLastRequireIndex = getLastRequireIndex;
//# sourceMappingURL=ast-utils.js.map