lite.mjs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. const FIREFOX_SAFARI_STACK_REGEXP = /(^|@)\S+:\d+/;
  2. const CHROME_IE_STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m;
  3. const SAFARI_NATIVE_CODE_REGEXP = /^(eval@)?(\[native code\])?$/;
  4. function parse(error, options) {
  5. if (typeof error.stacktrace !== "undefined" || typeof error["opera#sourceloc"] !== "undefined")
  6. return parseOpera(error, options);
  7. else if (error.stack && error.stack.match(CHROME_IE_STACK_REGEXP))
  8. return parseV8OrIE(error, options);
  9. else if (error.stack)
  10. return parseFFOrSafari(error, options);
  11. else if (options?.allowEmpty)
  12. return [];
  13. else
  14. throw new Error("Cannot parse given Error object");
  15. }
  16. function parseStack(stackString, options) {
  17. if (stackString.match(CHROME_IE_STACK_REGEXP))
  18. return parseV8OrIeString(stackString, options);
  19. else
  20. return parseFFOrSafariString(stackString, options);
  21. }
  22. function extractLocation(urlLike) {
  23. if (!urlLike.includes(":"))
  24. return [urlLike, void 0, void 0];
  25. const regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
  26. const parts = regExp.exec(urlLike.replace(/[()]/g, ""));
  27. return [parts[1], parts[2] || void 0, parts[3] || void 0];
  28. }
  29. function applySlice(lines, options) {
  30. if (options && options.slice != null) {
  31. if (Array.isArray(options.slice))
  32. return lines.slice(options.slice[0], options.slice[1]);
  33. return lines.slice(0, options.slice);
  34. }
  35. return lines;
  36. }
  37. function parseV8OrIE(error, options) {
  38. return parseV8OrIeString(error.stack, options);
  39. }
  40. function parseV8OrIeString(stack, options) {
  41. const filtered = applySlice(
  42. stack.split("\n").filter((line) => {
  43. return !!line.match(CHROME_IE_STACK_REGEXP);
  44. }),
  45. options
  46. );
  47. return filtered.map((line) => {
  48. if (line.includes("(eval ")) {
  49. line = line.replace(/eval code/g, "eval").replace(/(\(eval at [^()]*)|(,.*$)/g, "");
  50. }
  51. let sanitizedLine = line.replace(/^\s+/, "").replace(/\(eval code/g, "(").replace(/^.*?\s+/, "");
  52. const location = sanitizedLine.match(/ (\(.+\)$)/);
  53. sanitizedLine = location ? sanitizedLine.replace(location[0], "") : sanitizedLine;
  54. const locationParts = extractLocation(location ? location[1] : sanitizedLine);
  55. const functionName = location && sanitizedLine || void 0;
  56. const fileName = ["eval", "<anonymous>"].includes(locationParts[0]) ? void 0 : locationParts[0];
  57. return {
  58. function: functionName,
  59. file: fileName,
  60. line: locationParts[1] ? +locationParts[1] : void 0,
  61. col: locationParts[2] ? +locationParts[2] : void 0,
  62. raw: line
  63. };
  64. });
  65. }
  66. function parseFFOrSafari(error, options) {
  67. return parseFFOrSafariString(error.stack, options);
  68. }
  69. function parseFFOrSafariString(stack, options) {
  70. const filtered = applySlice(
  71. stack.split("\n").filter((line) => {
  72. return !line.match(SAFARI_NATIVE_CODE_REGEXP);
  73. }),
  74. options
  75. );
  76. return filtered.map((line) => {
  77. if (line.includes(" > eval"))
  78. line = line.replace(/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g, ":$1");
  79. if (!line.includes("@") && !line.includes(":")) {
  80. return {
  81. function: line
  82. };
  83. } else {
  84. const functionNameRegex = /(([^\n\r"\u2028\u2029]*".[^\n\r"\u2028\u2029]*"[^\n\r@\u2028\u2029]*(?:@[^\n\r"\u2028\u2029]*"[^\n\r@\u2028\u2029]*)*(?:[\n\r\u2028\u2029][^@]*)?)?[^@]*)@/;
  85. const matches = line.match(functionNameRegex);
  86. const functionName = matches && matches[1] ? matches[1] : void 0;
  87. const locationParts = extractLocation(line.replace(functionNameRegex, ""));
  88. return {
  89. function: functionName,
  90. file: locationParts[0],
  91. line: locationParts[1] ? +locationParts[1] : void 0,
  92. col: locationParts[2] ? +locationParts[2] : void 0,
  93. raw: line
  94. };
  95. }
  96. });
  97. }
  98. function parseOpera(e, options) {
  99. if (!e.stacktrace || e.message.includes("\n") && e.message.split("\n").length > e.stacktrace.split("\n").length)
  100. return parseOpera9(e);
  101. else if (!e.stack)
  102. return parseOpera10(e);
  103. else
  104. return parseOpera11(e, options);
  105. }
  106. function parseOpera9(e, options) {
  107. const lineRE = /Line (\d+).*script (?:in )?(\S+)/i;
  108. const lines = e.message.split("\n");
  109. const result = [];
  110. for (let i = 2, len = lines.length; i < len; i += 2) {
  111. const match = lineRE.exec(lines[i]);
  112. if (match) {
  113. result.push({
  114. file: match[2],
  115. line: +match[1],
  116. raw: lines[i]
  117. });
  118. }
  119. }
  120. return applySlice(result, options);
  121. }
  122. function parseOpera10(e, options) {
  123. const lineRE = /Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i;
  124. const lines = e.stacktrace.split("\n");
  125. const result = [];
  126. for (let i = 0, len = lines.length; i < len; i += 2) {
  127. const match = lineRE.exec(lines[i]);
  128. if (match) {
  129. result.push({
  130. function: match[3] || void 0,
  131. file: match[2],
  132. line: match[1] ? +match[1] : void 0,
  133. raw: lines[i]
  134. });
  135. }
  136. }
  137. return applySlice(result, options);
  138. }
  139. function parseOpera11(error, options) {
  140. const filtered = applySlice(
  141. // @ts-expect-error missing stack property
  142. error.stack.split("\n").filter((line) => {
  143. return !!line.match(FIREFOX_SAFARI_STACK_REGEXP) && !line.match(/^Error created at/);
  144. }),
  145. options
  146. );
  147. return filtered.map((line) => {
  148. const tokens = line.split("@");
  149. const locationParts = extractLocation(tokens.pop());
  150. const functionCall = tokens.shift() || "";
  151. const functionName = functionCall.replace(/<anonymous function(: (\w+))?>/, "$2").replace(/\([^)]*\)/g, "") || void 0;
  152. let argsRaw;
  153. if (functionCall.match(/\(([^)]*)\)/))
  154. argsRaw = functionCall.replace(/^[^(]+\(([^)]*)\)$/, "$1");
  155. const args = argsRaw === void 0 || argsRaw === "[arguments not available]" ? void 0 : argsRaw.split(",");
  156. return {
  157. function: functionName,
  158. args,
  159. file: locationParts[0],
  160. line: locationParts[1] ? +locationParts[1] : void 0,
  161. col: locationParts[2] ? +locationParts[2] : void 0,
  162. raw: line
  163. };
  164. });
  165. }
  166. export { extractLocation, parse, parseFFOrSafari, parseFFOrSafariString, parseOpera, parseOpera10, parseOpera11, parseOpera9, parseStack, parseV8OrIE, parseV8OrIeString };