devErrorSymbolicationEventProcessor.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. var {
  2. _nullishCoalesce,
  3. _optionalChain
  4. } = require('@sentry/utils');
  5. Object.defineProperty(exports, '__esModule', { value: true });
  6. const utils = require('@sentry/utils');
  7. const stackTraceParser = require('stacktrace-parser');
  8. const globalWithInjectedValues = utils.GLOBAL_OBJ
  9. ;
  10. async function resolveStackFrame(
  11. frame,
  12. error,
  13. ) {
  14. try {
  15. if (!(_optionalChain([frame, 'access', _ => _.file, 'optionalAccess', _2 => _2.startsWith, 'call', _3 => _3('webpack-internal:')]) || _optionalChain([frame, 'access', _4 => _4.file, 'optionalAccess', _5 => _5.startsWith, 'call', _6 => _6('file:')]))) {
  16. return null;
  17. }
  18. const params = new URLSearchParams();
  19. params.append('isServer', String(false)); // doesn't matter since it is overwritten by isAppDirectory
  20. params.append('isEdgeServer', String(false)); // doesn't matter since it is overwritten by isAppDirectory
  21. params.append('isAppDirectory', String(true)); // will force server to do more thorough checking
  22. params.append('errorMessage', error.toString());
  23. Object.keys(frame).forEach(key => {
  24. params.append(key, (_nullishCoalesce(frame[key ], () => ( ''))).toString());
  25. });
  26. let basePath = _nullishCoalesce(globalWithInjectedValues.__sentryBasePath, () => ( ''));
  27. // Prefix the basepath with a slash if it doesn't have one
  28. if (basePath !== '' && !basePath.match(/^\//)) {
  29. basePath = `/${basePath}`;
  30. }
  31. const controller = new AbortController();
  32. const timer = setTimeout(() => controller.abort(), 3000);
  33. const res = await fetch(
  34. `${
  35. // eslint-disable-next-line no-restricted-globals
  36. typeof window === 'undefined' ? 'http://localhost:3000' : '' // TODO: handle the case where users define a different port
  37. }${basePath}/__nextjs_original-stack-frame?${params.toString()}`,
  38. {
  39. signal: controller.signal,
  40. },
  41. ).finally(() => {
  42. clearTimeout(timer);
  43. });
  44. if (!res.ok || res.status === 204) {
  45. return null;
  46. }
  47. const body = await res.json();
  48. return {
  49. originalCodeFrame: body.originalCodeFrame,
  50. originalStackFrame: body.originalStackFrame,
  51. };
  52. } catch (e) {
  53. return null;
  54. }
  55. }
  56. function parseOriginalCodeFrame(codeFrame)
  57. {
  58. const preProcessedLines = codeFrame
  59. // Remove ASCII control characters that are used for syntax highlighting
  60. .replace(
  61. // eslint-disable-next-line no-control-regex
  62. /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, // https://stackoverflow.com/a/29497680
  63. '',
  64. )
  65. .split('\n')
  66. // Remove line that is supposed to indicate where the error happened
  67. .filter(line => !line.match(/^\s*\|/))
  68. // Find the error line
  69. .map(line => ({
  70. line,
  71. isErrorLine: !!line.match(/^>/),
  72. }))
  73. // Remove the leading part that is just for prettier output
  74. .map(lineObj => ({
  75. ...lineObj,
  76. line: lineObj.line.replace(/^.*\|/, ''),
  77. }));
  78. const preContextLines = [];
  79. let contextLine = undefined;
  80. const postContextLines = [];
  81. let reachedContextLine = false;
  82. for (const preProcessedLine of preProcessedLines) {
  83. if (preProcessedLine.isErrorLine) {
  84. contextLine = preProcessedLine.line;
  85. reachedContextLine = true;
  86. } else if (reachedContextLine) {
  87. postContextLines.push(preProcessedLine.line);
  88. } else {
  89. preContextLines.push(preProcessedLine.line);
  90. }
  91. }
  92. return {
  93. contextLine,
  94. preContextLines,
  95. postContextLines,
  96. };
  97. }
  98. /**
  99. * Event processor that will symbolicate errors by using the webpack/nextjs dev server that is used to show stack traces
  100. * in the dev overlay.
  101. */
  102. async function devErrorSymbolicationEventProcessor(event, hint) {
  103. // Due to changes across Next.js versions, there are a million things that can go wrong here so we just try-catch the // entire event processor.Symbolicated stack traces are just a nice to have.
  104. try {
  105. if (hint.originalException && hint.originalException instanceof Error && hint.originalException.stack) {
  106. const frames = stackTraceParser.parse(hint.originalException.stack);
  107. const resolvedFrames = await Promise.all(
  108. frames.map(frame => resolveStackFrame(frame, hint.originalException )),
  109. );
  110. if (_optionalChain([event, 'access', _7 => _7.exception, 'optionalAccess', _8 => _8.values, 'optionalAccess', _9 => _9[0], 'access', _10 => _10.stacktrace, 'optionalAccess', _11 => _11.frames])) {
  111. event.exception.values[0].stacktrace.frames = event.exception.values[0].stacktrace.frames.map(
  112. (frame, i, frames) => {
  113. const resolvedFrame = resolvedFrames[frames.length - 1 - i];
  114. if (!resolvedFrame || !resolvedFrame.originalStackFrame || !resolvedFrame.originalCodeFrame) {
  115. return {
  116. ...frame,
  117. platform: _optionalChain([frame, 'access', _12 => _12.filename, 'optionalAccess', _13 => _13.startsWith, 'call', _14 => _14('node:internal')]) ? 'nodejs' : undefined, // simple hack that will prevent a source mapping error from showing up
  118. in_app: false,
  119. };
  120. }
  121. const { contextLine, preContextLines, postContextLines } = parseOriginalCodeFrame(
  122. resolvedFrame.originalCodeFrame,
  123. );
  124. return {
  125. ...frame,
  126. pre_context: preContextLines,
  127. context_line: contextLine,
  128. post_context: postContextLines,
  129. function: resolvedFrame.originalStackFrame.methodName,
  130. filename: resolvedFrame.originalStackFrame.file || undefined,
  131. lineno: resolvedFrame.originalStackFrame.lineNumber || undefined,
  132. colno: resolvedFrame.originalStackFrame.column || undefined,
  133. };
  134. },
  135. );
  136. }
  137. }
  138. } catch (e) {
  139. return event;
  140. }
  141. return event;
  142. }
  143. exports.devErrorSymbolicationEventProcessor = devErrorSymbolicationEventProcessor;
  144. //# sourceMappingURL=devErrorSymbolicationEventProcessor.js.map