locations.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. "use strict";
  2. /*
  3. * eslint-plugin-sonarjs
  4. * Copyright (C) 2018-2021 SonarSource SA
  5. * mailto:info AT sonarsource DOT com
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 3 of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public License
  18. * along with this program; if not, write to the Free Software Foundation,
  19. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  20. */
  21. Object.defineProperty(exports, "__esModule", { value: true });
  22. exports.getFirstToken = exports.getFirstTokenAfter = exports.toSecondaryLocation = exports.issueLocation = exports.report = exports.getMainFunctionTokenLocation = void 0;
  23. /**
  24. * Returns a location of the "main" function token:
  25. * - function name for a function declaration, method or accessor
  26. * - "function" keyword for a function expression
  27. * - "=>" for an arrow function
  28. */
  29. function getMainFunctionTokenLocation(fn, parent, context) {
  30. let location;
  31. if (fn.type === 'FunctionDeclaration') {
  32. // `fn.id` can be null when it is `export default function` (despite of the @types/TSESTree definition)
  33. if (fn.id) {
  34. location = fn.id.loc;
  35. }
  36. else {
  37. const token = getTokenByValue(fn, 'function', context);
  38. location = token && token.loc;
  39. }
  40. }
  41. else if (fn.type === 'FunctionExpression') {
  42. if (parent && (parent.type === 'MethodDefinition' || parent.type === 'Property')) {
  43. location = parent.key.loc;
  44. }
  45. else {
  46. const token = getTokenByValue(fn, 'function', context);
  47. location = token && token.loc;
  48. }
  49. }
  50. else if (fn.type === 'ArrowFunctionExpression') {
  51. const token = context
  52. .getSourceCode()
  53. .getTokensBefore(fn.body)
  54. .reverse()
  55. .find(token => token.value === '=>');
  56. location = token && token.loc;
  57. }
  58. return location;
  59. }
  60. exports.getMainFunctionTokenLocation = getMainFunctionTokenLocation;
  61. /**
  62. * Wrapper for `context.report`, supporting secondary locations and cost.
  63. * Encode those extra information in the issue message when rule is executed
  64. * in Sonar* environment.
  65. */
  66. function report(context, reportDescriptor, secondaryLocations, message, cost) {
  67. if (context.options[context.options.length - 1] !== 'sonar-runtime') {
  68. context.report(reportDescriptor);
  69. return;
  70. }
  71. const encodedMessage = {
  72. secondaryLocations,
  73. message: expandMessage(message, reportDescriptor.data),
  74. cost,
  75. };
  76. reportDescriptor.messageId = 'sonarRuntime';
  77. if (reportDescriptor.data === undefined) {
  78. reportDescriptor.data = {};
  79. }
  80. reportDescriptor.data.sonarRuntimeData =
  81. JSON.stringify(encodedMessage);
  82. context.report(reportDescriptor);
  83. }
  84. exports.report = report;
  85. function expandMessage(message, reportDescriptorData) {
  86. let expandedMessage = message;
  87. if (reportDescriptorData !== undefined) {
  88. for (const [key, value] of Object.entries(reportDescriptorData)) {
  89. expandedMessage = replaceAll(expandedMessage, `{{${key}}}`, value.toString());
  90. }
  91. }
  92. return expandedMessage;
  93. }
  94. function replaceAll(target, search, replacement) {
  95. return target.split(search).join(replacement);
  96. }
  97. /**
  98. * Converts `SourceLocation` range into `IssueLocation`
  99. */
  100. function issueLocation(startLoc, endLoc = startLoc, message = '', data = {}) {
  101. const issueLocation = {
  102. line: startLoc.start.line,
  103. column: startLoc.start.column,
  104. endLine: endLoc.end.line,
  105. endColumn: endLoc.end.column,
  106. message,
  107. };
  108. if (data !== undefined && Object.keys(data).length > 0) {
  109. issueLocation.data = data;
  110. }
  111. return issueLocation;
  112. }
  113. exports.issueLocation = issueLocation;
  114. function toSecondaryLocation(locationHolder, message) {
  115. const { loc } = locationHolder;
  116. return {
  117. message,
  118. column: loc.start.column,
  119. line: loc.start.line,
  120. endColumn: loc.end.column,
  121. endLine: loc.end.line,
  122. };
  123. }
  124. exports.toSecondaryLocation = toSecondaryLocation;
  125. function getTokenByValue(node, value, context) {
  126. return context
  127. .getSourceCode()
  128. .getTokens(node)
  129. .find(token => token.value === value);
  130. }
  131. function getFirstTokenAfter(node, context) {
  132. return context.getSourceCode().getTokenAfter(node);
  133. }
  134. exports.getFirstTokenAfter = getFirstTokenAfter;
  135. function getFirstToken(node, context) {
  136. return context.getSourceCode().getTokens(node)[0];
  137. }
  138. exports.getFirstToken = getFirstToken;
  139. //# sourceMappingURL=locations.js.map