eslint-plugin-sonarjs
Version:
SonarJS rules for ESLint
147 lines (146 loc) • 5.42 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.encodeContents = encodeContents;
exports.toEncodedMessage = toEncodedMessage;
exports.toSecondaryLocation = toSecondaryLocation;
exports.report = report;
exports.expandMessage = expandMessage;
exports.getMainFunctionTokenLocation = getMainFunctionTokenLocation;
exports.getFirstTokenAfter = getFirstTokenAfter;
exports.getFirstToken = getFirstToken;
function encodeContents(message, secondaryLocations, cost) {
return JSON.stringify({
message,
secondaryLocations,
cost,
});
}
/**
* Encodes an ESLint descriptor message with secondary locations
*
* The encoding consists in stringifying a JavaScript object with
* `JSON.stringify` that includes the ESLint's descriptor message
* along with second location information: message and location.
*
* This encoded message is eventually decoded by the linter wrapper
* on the condition that the rule definition of the flagged problem
* defines the internal `sonar-runtime` parameter in its schema.
*
* @param reportDescriptor the ESLint report descriptor
* @param secondaryLocations the secondary locations
* @param cost the optional cost to fix
* @returns the encoded message with secondary locations
*/
function toEncodedMessage(reportDescriptor, secondaryLocations, cost) {
if (!('message' in reportDescriptor)) {
throw new Error('Field "message" is mandatory in the report descriptor for sonar runtime encoding');
}
if (reportDescriptor.data === undefined) {
reportDescriptor.data = {};
}
const { message: _, ...rest } = reportDescriptor;
return {
...rest,
messageId: 'sonarRuntime',
data: {
sonarRuntimeData: encodeContents(expandMessage(reportDescriptor.message, reportDescriptor.data), secondaryLocations, cost),
...reportDescriptor.data,
},
};
}
function toSecondaryLocation(startLoc, endLoc = startLoc, message) {
if (!startLoc.loc) {
throw new Error('Invalid secondary location');
}
const endLocation = typeof endLoc !== 'string' && endLoc.loc ? endLoc.loc : startLoc.loc;
return {
message: typeof endLoc === 'string' ? endLoc : message,
column: startLoc.loc.start.column,
line: startLoc.loc.start.line,
endColumn: endLocation.end.column,
endLine: endLocation.end.line,
};
}
/**
* Wrapper for `context.report`, supporting secondary locations and cost.
* Encode those extra information in the issue message when rule is executed
* in Sonar* environment.
*/
function report(context, reportDescriptor, secondaryLocations = [], cost) {
if (context.options[context.options.length - 1] !== 'sonar-runtime') {
if ('message' in reportDescriptor && 'messageId' in reportDescriptor) {
const { message: _, ...rest } = reportDescriptor;
context.report(rest);
}
else if ('message' in reportDescriptor && 'data' in reportDescriptor) {
const { data, ...rest } = reportDescriptor;
context.report({
...rest,
message: expandMessage(rest.message, data),
});
}
else {
context.report(reportDescriptor);
}
}
else {
context.report(toEncodedMessage(reportDescriptor, secondaryLocations, cost));
}
}
function expandMessage(message, reportDescriptorData) {
let expandedMessage = message;
if (reportDescriptorData !== undefined) {
for (const [key, value] of Object.entries(reportDescriptorData)) {
expandedMessage = replaceAll(expandedMessage, `{{${key}}}`, value.toString());
}
}
return expandedMessage;
}
function replaceAll(target, search, replacement) {
return target.split(search).join(replacement);
}
/**
* Returns a location of the "main" function token:
* - function name for a function declaration, method or accessor
* - "function" keyword for a function expression
* - "=>" for an arrow function
*/
function getMainFunctionTokenLocation(fn, parent, context) {
let location;
if (fn.type === 'FunctionDeclaration') {
// `fn.id` can be null when it is `export default function` (despite of the @types/TSESTree definition)
if (fn.id) {
location = fn.id.loc;
}
else {
const token = getTokenByValue(fn, 'function', context);
location = token?.loc;
}
}
else if (fn.type === 'FunctionExpression') {
if (parent && (parent.type === 'MethodDefinition' || parent.type === 'Property')) {
location = parent.key.loc;
}
else {
const token = getTokenByValue(fn, 'function', context);
location = token?.loc;
}
}
else if (fn.type === 'ArrowFunctionExpression') {
const token = context.sourceCode
.getTokensBefore(fn.body)
.reverse()
.find(token => token.value === '=>');
location = token?.loc;
}
return location;
}
function getTokenByValue(node, value, context) {
return context.sourceCode.getTokens(node).find(token => token.value === value);
}
function getFirstTokenAfter(node, context) {
return context.sourceCode.getTokenAfter(node);
}
function getFirstToken(node, context) {
return context.sourceCode.getTokens(node)[0];
}
;