withSentryConfig.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import { _optionalChain } from '@sentry/utils';
  2. import { isThenable } from '@sentry/utils';
  3. import { constructWebpackConfigFunction } from './webpack.js';
  4. let showedExportModeTunnelWarning = false;
  5. /**
  6. * Add Sentry options to the config to be exported from the user's `next.config.js` file.
  7. *
  8. * @param exportedUserNextConfig The existing config to be exported prior to adding Sentry
  9. * @param userSentryWebpackPluginOptions Configuration for SentryWebpackPlugin
  10. * @param sentryOptions Optional additional options to add as alternative to `sentry` property of config
  11. * @returns The modified config to be exported
  12. */
  13. function withSentryConfig(
  14. exportedUserNextConfig = {},
  15. userSentryWebpackPluginOptions = {},
  16. sentryOptions,
  17. ) {
  18. if (typeof exportedUserNextConfig === 'function') {
  19. return function ( ...webpackConfigFunctionArgs) {
  20. const maybeUserNextConfigObject = exportedUserNextConfig.apply(
  21. this,
  22. webpackConfigFunctionArgs,
  23. );
  24. if (isThenable(maybeUserNextConfigObject)) {
  25. return maybeUserNextConfigObject.then(function (userNextConfigObject) {
  26. const userSentryOptions = { ...userNextConfigObject.sentry, ...sentryOptions };
  27. return getFinalConfigObject(userNextConfigObject, userSentryOptions, userSentryWebpackPluginOptions);
  28. });
  29. }
  30. // Reassign for naming-consistency sake.
  31. const userNextConfigObject = maybeUserNextConfigObject;
  32. const userSentryOptions = { ...userNextConfigObject.sentry, ...sentryOptions };
  33. return getFinalConfigObject(userNextConfigObject, userSentryOptions, userSentryWebpackPluginOptions);
  34. };
  35. } else {
  36. const userSentryOptions = { ...exportedUserNextConfig.sentry, ...sentryOptions };
  37. return getFinalConfigObject(exportedUserNextConfig, userSentryOptions, userSentryWebpackPluginOptions);
  38. }
  39. }
  40. // Modify the materialized object form of the user's next config by deleting the `sentry` property and wrapping the
  41. // `webpack` property
  42. function getFinalConfigObject(
  43. incomingUserNextConfigObject,
  44. userSentryOptions,
  45. userSentryWebpackPluginOptions,
  46. ) {
  47. // Next 12.2.3+ warns about non-canonical properties on `userNextConfig`.
  48. delete incomingUserNextConfigObject.sentry;
  49. if (_optionalChain([userSentryOptions, 'optionalAccess', _ => _.tunnelRoute])) {
  50. if (incomingUserNextConfigObject.output === 'export') {
  51. if (!showedExportModeTunnelWarning) {
  52. showedExportModeTunnelWarning = true;
  53. // eslint-disable-next-line no-console
  54. console.warn(
  55. '[@sentry/nextjs] The Sentry Next.js SDK `tunnelRoute` option will not work in combination with Next.js static exports. The `tunnelRoute` option uses serverside features that cannot be accessed in export mode. If you still want to tunnel Sentry events, set up your own tunnel: https://docs.sentry.io/platforms/javascript/troubleshooting/#using-the-tunnel-option',
  56. );
  57. }
  58. } else {
  59. setUpTunnelRewriteRules(incomingUserNextConfigObject, userSentryOptions.tunnelRoute);
  60. }
  61. }
  62. return {
  63. ...incomingUserNextConfigObject,
  64. webpack: constructWebpackConfigFunction(
  65. incomingUserNextConfigObject,
  66. userSentryWebpackPluginOptions,
  67. userSentryOptions,
  68. ),
  69. };
  70. }
  71. /**
  72. * Injects rewrite rules into the Next.js config provided by the user to tunnel
  73. * requests from the `tunnelPath` to Sentry.
  74. *
  75. * See https://nextjs.org/docs/api-reference/next.config.js/rewrites.
  76. */
  77. function setUpTunnelRewriteRules(userNextConfig, tunnelPath) {
  78. const originalRewrites = userNextConfig.rewrites;
  79. // This function doesn't take any arguments at the time of writing but we future-proof
  80. // here in case Next.js ever decides to pass some
  81. userNextConfig.rewrites = async (...args) => {
  82. const injectedRewrite = {
  83. // Matched rewrite routes will look like the following: `[tunnelPath]?o=[orgid]&p=[projectid]`
  84. // Nextjs will automatically convert `source` into a regex for us
  85. source: `${tunnelPath}(/?)`,
  86. has: [
  87. {
  88. type: 'query',
  89. key: 'o', // short for orgId - we keep it short so matching is harder for ad-blockers
  90. value: '(?<orgid>\\d*)',
  91. },
  92. {
  93. type: 'query',
  94. key: 'p', // short for projectId - we keep it short so matching is harder for ad-blockers
  95. value: '(?<projectid>\\d*)',
  96. },
  97. ],
  98. destination: 'https://o:orgid.ingest.sentry.io/api/:projectid/envelope/?hsts=0',
  99. };
  100. if (typeof originalRewrites !== 'function') {
  101. return [injectedRewrite];
  102. }
  103. // @ts-expect-error Expected 0 arguments but got 1 - this is from the future-proofing mentioned above, so we don't care about it
  104. const originalRewritesResult = await originalRewrites(...args);
  105. if (Array.isArray(originalRewritesResult)) {
  106. return [injectedRewrite, ...originalRewritesResult];
  107. } else {
  108. return {
  109. ...originalRewritesResult,
  110. beforeFiles: [injectedRewrite, ...(originalRewritesResult.beforeFiles || [])],
  111. };
  112. }
  113. };
  114. }
  115. export { withSentryConfig };
  116. //# sourceMappingURL=withSentryConfig.js.map