UNPKG

@sentry/nextjs

Version:
578 lines (574 loc) 27.2 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const core = require('@sentry/core'); const fs = require('fs'); const module$1 = require('module'); const path = require('path'); const getBuildPluginOptions = require('./getBuildPluginOptions.js'); const webpack = require('@sentry/server-utils/orchestrion/webpack'); const util = require('./util.js'); let showedMissingGlobalErrorWarningMsg = false; function constructWebpackConfigFunction({ userNextConfig = {}, userSentryOptions = {}, releaseName, routeManifest, nextJsVersion, useRunAfterProductionCompileHook, vercelCronsConfigResult }) { return function newWebpackFunction(incomingConfig, buildContext) { const { isServer, dev: isDev, dir: projectDir } = buildContext; const runtime = isServer ? buildContext.nextRuntime === "edge" ? "edge" : "server" : "client"; const pageExtensions = userNextConfig.pageExtensions || ["tsx", "ts", "jsx", "js"]; const dotPrefixedPageExtensions = pageExtensions.map((ext) => `.${ext}`); const pageExtensionRegex = pageExtensions.map(core.escapeStringForRegex).join("|"); const nextVersion = nextJsVersion || util.getNextjsVersion(); const { major } = core.parseSemver(nextVersion || ""); const instrumentationFile = getInstrumentationFile(projectDir, dotPrefixedPageExtensions.concat([".ts", ".js"])); if (runtime !== "client") { warnAboutDeprecatedConfigFiles(projectDir, instrumentationFile, runtime); } if (runtime === "server") { if (major && major >= 15) { warnAboutMissingOnRequestErrorHandler(instrumentationFile); } } let rawNewConfig = { ...incomingConfig }; if ("webpack" in userNextConfig && typeof userNextConfig.webpack === "function") { rawNewConfig = userNextConfig.webpack(rawNewConfig, buildContext); } const newConfig = setUpModuleRules(rawNewConfig); const { strategy: cronsStrategy, config: cronsConfig } = vercelCronsConfigResult; const vercelCronsConfigForGlobal = cronsStrategy === "spans" ? cronsConfig : void 0; const vercelCronsConfigForWrapper = cronsStrategy === "wrapper" ? cronsConfig : void 0; addValueInjectionLoader({ newConfig, userNextConfig, userSentryOptions, buildContext, releaseName, routeManifest, nextJsVersion, vercelCronsConfig: vercelCronsConfigForGlobal }); addOtelWarningIgnoreRule(newConfig); if (major && major === 13 && runtime === "edge" && isDev) { addEdgeRuntimePolyfills(newConfig, buildContext); } let pagesDirPath; const maybePagesDirPath = path.join(projectDir, "pages"); const maybeSrcPagesDirPath = path.join(projectDir, "src", "pages"); if (fs.existsSync(maybePagesDirPath) && fs.lstatSync(maybePagesDirPath).isDirectory()) { pagesDirPath = maybePagesDirPath; } else if (fs.existsSync(maybeSrcPagesDirPath) && fs.lstatSync(maybeSrcPagesDirPath).isDirectory()) { pagesDirPath = maybeSrcPagesDirPath; } let appDirPath; const maybeAppDirPath = path.join(projectDir, "app"); const maybeSrcAppDirPath = path.join(projectDir, "src", "app"); if (fs.existsSync(maybeAppDirPath) && fs.lstatSync(maybeAppDirPath).isDirectory()) { appDirPath = maybeAppDirPath; } else if (fs.existsSync(maybeSrcAppDirPath) && fs.lstatSync(maybeSrcAppDirPath).isDirectory()) { appDirPath = maybeSrcAppDirPath; } const apiRoutesPath = pagesDirPath ? path.join(pagesDirPath, "api") : void 0; const middlewareLocationFolder = pagesDirPath ? path.join(pagesDirPath, "..") : appDirPath ? path.join(appDirPath, "..") : projectDir; const staticWrappingLoaderOptions = { appDir: appDirPath, pagesDir: pagesDirPath, pageExtensionRegex, excludeServerRoutes: userSentryOptions.webpack?.excludeServerRoutes, nextjsRequestAsyncStorageModulePath: getRequestAsyncStorageModuleLocation( projectDir, rawNewConfig.resolve?.modules ), isDev }; const normalizeLoaderResourcePath = (resourcePath) => { let absoluteResourcePath; if (path.isAbsolute(resourcePath)) { absoluteResourcePath = resourcePath; } else { absoluteResourcePath = path.join(projectDir, resourcePath); } return path.normalize(absoluteResourcePath); }; const isPageResource = (resourcePath) => { const normalizedAbsoluteResourcePath = normalizeLoaderResourcePath(resourcePath); return pagesDirPath !== void 0 && normalizedAbsoluteResourcePath.startsWith(pagesDirPath + path.sep) && !normalizedAbsoluteResourcePath.startsWith(apiRoutesPath + path.sep) && dotPrefixedPageExtensions.some((ext) => normalizedAbsoluteResourcePath.endsWith(ext)); }; const isApiRouteResource = (resourcePath) => { const normalizedAbsoluteResourcePath = normalizeLoaderResourcePath(resourcePath); return normalizedAbsoluteResourcePath.startsWith(apiRoutesPath + path.sep) && dotPrefixedPageExtensions.some((ext) => normalizedAbsoluteResourcePath.endsWith(ext)); }; const possibleMiddlewareLocations = pageExtensions.flatMap((middlewareFileEnding) => { return [ path.join(middlewareLocationFolder, `middleware.${middlewareFileEnding}`), path.join(middlewareLocationFolder, `proxy.${middlewareFileEnding}`) ]; }); const isMiddlewareResource = (resourcePath) => { const normalizedAbsoluteResourcePath = normalizeLoaderResourcePath(resourcePath); return possibleMiddlewareLocations.includes(normalizedAbsoluteResourcePath); }; const isServerComponentResource = (resourcePath) => { const normalizedAbsoluteResourcePath = normalizeLoaderResourcePath(resourcePath); return appDirPath !== void 0 && normalizedAbsoluteResourcePath.startsWith(appDirPath + path.sep) && !!normalizedAbsoluteResourcePath.match( // oxlint-disable-next-line sdk/no-regexp-constructor new RegExp(`[\\\\/](page|layout|loading|head|not-found)\\.(${pageExtensionRegex})$`) ); }; const isRouteHandlerResource = (resourcePath) => { const normalizedAbsoluteResourcePath = normalizeLoaderResourcePath(resourcePath); return appDirPath !== void 0 && normalizedAbsoluteResourcePath.startsWith(appDirPath + path.sep) && !!normalizedAbsoluteResourcePath.match( // oxlint-disable-next-line sdk/no-regexp-constructor new RegExp(`[\\\\/]route\\.(${pageExtensionRegex})$`) ); }; if (isServer && userSentryOptions.webpack?.autoInstrumentServerFunctions !== false) { newConfig.module.rules.unshift({ test: isPageResource, use: [ { loader: path.resolve(__dirname, "loaders", "wrappingLoader.js"), options: { ...staticWrappingLoaderOptions, wrappingTargetKind: "page" } } ] }); newConfig.module.rules.unshift({ test: isApiRouteResource, use: [ { loader: path.resolve(__dirname, "loaders", "wrappingLoader.js"), options: { ...staticWrappingLoaderOptions, vercelCronsConfig: vercelCronsConfigForWrapper, wrappingTargetKind: "api-route" } } ] }); const canWrapStandaloneMiddleware = userNextConfig.output !== "standalone" || !major || major < 16; if ((userSentryOptions.webpack?.autoInstrumentMiddleware ?? true) && canWrapStandaloneMiddleware) { newConfig.module.rules.unshift({ test: isMiddlewareResource, use: [ { loader: path.resolve(__dirname, "loaders", "wrappingLoader.js"), options: { ...staticWrappingLoaderOptions, wrappingTargetKind: "middleware" } } ] }); } } if (isServer && userSentryOptions.webpack?.autoInstrumentAppDirectory !== false) { newConfig.module.rules.unshift({ test: isServerComponentResource, use: [ { loader: path.resolve(__dirname, "loaders", "wrappingLoader.js"), options: { ...staticWrappingLoaderOptions, wrappingTargetKind: "server-component" } } ] }); newConfig.module.rules.unshift({ test: isRouteHandlerResource, use: [ { loader: path.resolve(__dirname, "loaders", "wrappingLoader.js"), options: { ...staticWrappingLoaderOptions, wrappingTargetKind: "route-handler" } } ] }); } if (appDirPath) { const hasGlobalErrorFile = pageExtensions.map((extension) => `global-error.${extension}`).some((globalErrorFile) => fs.existsSync(path.join(appDirPath, globalErrorFile))); if (!hasGlobalErrorFile && !showedMissingGlobalErrorWarningMsg && !process.env.SENTRY_SUPPRESS_GLOBAL_ERROR_HANDLER_FILE_WARNING) { console.log( "[@sentry/nextjs] It seems like you don't have a global error handler set up. It is recommended that you add a 'global-error.js' file with Sentry instrumentation so that React rendering errors are reported to Sentry. Read more: https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/#react-render-errors-in-app-router (you can suppress this warning by setting SENTRY_SUPPRESS_GLOBAL_ERROR_HANDLER_FILE_WARNING=1 as environment variable)" ); showedMissingGlobalErrorWarningMsg = true; } } if (!isServer) { const origEntryProperty = newConfig.entry; newConfig.entry = async () => addSentryToClientEntryProperty(origEntryProperty, buildContext); const clientSentryConfigFileName = getClientSentryConfigFile(projectDir); if (clientSentryConfigFileName) { console.warn( `[@sentry/nextjs] DEPRECATION WARNING: It is recommended renaming your \`${clientSentryConfigFileName}\` file, or moving its content to \`instrumentation-client.ts\`. When using Turbopack \`${clientSentryConfigFileName}\` will no longer work. Read more about the \`instrumentation-client.ts\` file: https://nextjs.org/docs/app/api-reference/file-conventions/instrumentation-client` ); } } const isStaticExport = userNextConfig?.output === "export"; if (!(isDev || isStaticExport && isServer)) { const { sentryWebpackPlugin } = core.loadModule("@sentry/webpack-plugin", module) ?? {}; if (sentryWebpackPlugin) { if (!userSentryOptions.sourcemaps?.disable) { if (!newConfig.devtool) { core.debug.log(`[@sentry/nextjs] Automatically enabling source map generation for ${runtime} build.`); if (isServer) { newConfig.devtool = "source-map"; } else { newConfig.devtool = "hidden-source-map"; } } if (!isServer && userSentryOptions.sourcemaps?.deleteSourcemapsAfterUpload === void 0) { core.debug.warn( "[@sentry/nextjs] Source maps will be automatically deleted after being uploaded to Sentry. If you want to keep the source maps, set the `sourcemaps.deleteSourcemapsAfterUpload` option to false in `withSentryConfig()`. If you do not want to generate and upload sourcemaps at all, set the `sourcemaps.disable` option to true." ); userSentryOptions.sourcemaps = { ...userSentryOptions.sourcemaps, deleteSourcemapsAfterUpload: true }; } } newConfig.plugins = newConfig.plugins || []; const { config: userNextConfig2, dir, nextRuntime } = buildContext; const buildTool = isServer ? nextRuntime === "edge" ? "webpack-edge" : "webpack-nodejs" : "webpack-client"; const projectDir2 = getBuildPluginOptions.normalizePathForGlob(dir); const distDir = getBuildPluginOptions.normalizePathForGlob(userNextConfig2.distDir ?? ".next"); const distDirAbsPath = path.posix.join(projectDir2, distDir); const sentryWebpackPluginInstance = sentryWebpackPlugin( getBuildPluginOptions.getBuildPluginOptions({ sentryBuildOptions: userSentryOptions, releaseName, distDirAbsPath, buildTool, useRunAfterProductionCompileHook }) ); sentryWebpackPluginInstance._name = "sentry-webpack-plugin"; newConfig.plugins.push(sentryWebpackPluginInstance); } } if (userSentryOptions.webpack?.treeshake) { setupTreeshakingFromConfig(userSentryOptions, newConfig, buildContext); } newConfig.plugins = newConfig.plugins || []; newConfig.plugins.push( new buildContext.webpack.DefinePlugin({ __SENTRY_SERVER_MODULES__: JSON.stringify(util.getPackageModules(projectDir)) }) ); if (runtime === "server" && userSentryOptions._experimental?.useDiagnosticsChannelInjection) { newConfig.plugins.push(webpack.sentryOrchestrionWebpackPlugin()); } return newConfig; }; } async function addSentryToClientEntryProperty(currentEntryProperty, buildContext) { const { dir: projectDir, dev: isDevMode } = buildContext; const newEntryProperty = typeof currentEntryProperty === "function" ? await currentEntryProperty() : { ...currentEntryProperty }; const clientSentryConfigFileName = getClientSentryConfigFile(projectDir); const instrumentationClientFileName = getInstrumentationClientFile(projectDir); const filesToInject = []; if (clientSentryConfigFileName) { filesToInject.push(`./${clientSentryConfigFileName}`); } if (instrumentationClientFileName) { filesToInject.push(`./${instrumentationClientFileName}`); } for (const entryPointName in newEntryProperty) { if (entryPointName === "pages/_app" || // entrypoint for `/app` pages entryPointName === "main-app") { addFilesToWebpackEntryPoint(newEntryProperty, entryPointName, filesToInject, isDevMode); } } return newEntryProperty; } function getInstrumentationFile(projectDir, dotPrefixedExtensions) { const paths = dotPrefixedExtensions.flatMap((extension) => [ ["src", `instrumentation${extension}`], [`instrumentation${extension}`] ]); for (const pathSegments of paths) { try { return fs.readFileSync(path.resolve(projectDir, ...pathSegments), { encoding: "utf-8" }); } catch { } } return null; } function warnAboutMissingOnRequestErrorHandler(instrumentationFile) { if (!instrumentationFile) { if (!process.env.SENTRY_SUPPRESS_INSTRUMENTATION_FILE_WARNING) { console.warn( "[@sentry/nextjs] Could not find a Next.js instrumentation file. This indicates an incomplete configuration of the Sentry SDK. An instrumentation file is required for the Sentry SDK to be initialized on the server: https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/#create-initialization-config-files (you can suppress this warning by setting SENTRY_SUPPRESS_INSTRUMENTATION_FILE_WARNING=1 as environment variable)" ); } return; } if (!instrumentationFile.includes("onRequestError")) { console.warn( "[@sentry/nextjs] Could not find `onRequestError` hook in instrumentation file. This indicates outdated configuration of the Sentry SDK. Use `Sentry.captureRequestError` to instrument the `onRequestError` hook: https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/#errors-from-nested-react-server-components" ); } } function warnAboutDeprecatedConfigFiles(projectDir, instrumentationFile, platform) { const hasInstrumentationHookWithIndicationsOfSentry = instrumentationFile && (instrumentationFile.includes("@sentry/") || instrumentationFile.match(/sentry\.(server|edge)\.config(\.(ts|js))?/)); if (hasInstrumentationHookWithIndicationsOfSentry) { return; } for (const filename of [`sentry.${platform}.config.ts`, `sentry.${platform}.config.js`]) { if (fs.existsSync(path.resolve(projectDir, filename))) { console.warn( `[@sentry/nextjs] It appears you've configured a \`${filename}\` file. Please ensure to put this file's content into the \`register()\` function of a Next.js instrumentation file instead. To ensure correct functionality of the SDK, \`Sentry.init\` must be called inside of an instrumentation file. Learn more about setting up an instrumentation file in Next.js: https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation. You can safely delete the \`${filename}\` file afterward.` ); } } } function getClientSentryConfigFile(projectDir) { const possibilities = ["sentry.client.config.ts", "sentry.client.config.js"]; for (const filename of possibilities) { if (fs.existsSync(path.resolve(projectDir, filename))) { return filename; } } } function getInstrumentationClientFile(projectDir) { const possibilities = [ ["src", "instrumentation-client.js"], ["src", "instrumentation-client.ts"], ["instrumentation-client.js"], ["instrumentation-client.ts"] ]; for (const pathParts of possibilities) { if (fs.existsSync(path.resolve(projectDir, ...pathParts))) { return path.join(...pathParts); } } } function addFilesToWebpackEntryPoint(entryProperty, entryPointName, filesToInsert, isDevMode) { const currentEntryPoint = entryProperty[entryPointName]; let newEntryPoint = currentEntryPoint; if (typeof currentEntryPoint === "string" || Array.isArray(currentEntryPoint)) { newEntryPoint = Array.isArray(currentEntryPoint) ? currentEntryPoint : [currentEntryPoint]; if (newEntryPoint.some((entry) => filesToInsert.includes(entry))) { return; } if (isDevMode) { newEntryPoint.push(...filesToInsert); } else { newEntryPoint.unshift(...filesToInsert); } } else if (typeof currentEntryPoint === "object" && "import" in currentEntryPoint) { const currentImportValue = currentEntryPoint.import; const newImportValue = Array.isArray(currentImportValue) ? currentImportValue : [currentImportValue]; if (newImportValue.some((entry) => filesToInsert.includes(entry))) { return; } if (isDevMode) { newImportValue.push(...filesToInsert); } else { newImportValue.unshift(...filesToInsert); } newEntryPoint = { ...currentEntryPoint, import: newImportValue }; } else { console.error( "Sentry Logger [Error]:", `Could not inject SDK initialization code into entry point ${entryPointName}, as its current value is not in a recognized format. `, "Expected: string | Array<string> | { [key:string]: any, import: string | Array<string> }\n", `Got: ${currentEntryPoint}` ); } if (newEntryPoint) { entryProperty[entryPointName] = newEntryPoint; } } function setUpModuleRules(newConfig) { newConfig.module = { ...newConfig.module, rules: [...newConfig.module?.rules || []] }; return newConfig; } function addValueInjectionLoader({ newConfig, userNextConfig, userSentryOptions, buildContext, releaseName, routeManifest, nextJsVersion, vercelCronsConfig }) { const assetPrefix = userNextConfig.assetPrefix || userNextConfig.basePath || ""; const shouldCreateRelease = userSentryOptions.release?.create !== false; const releaseToInject = releaseName && shouldCreateRelease ? releaseName : void 0; const isomorphicValues = { // `rewritesTunnel` set by the user in Next.js config _sentryRewritesTunnelPath: userSentryOptions.tunnelRoute !== void 0 && userNextConfig.output !== "export" && typeof userSentryOptions.tunnelRoute === "string" ? `${userNextConfig.basePath ?? ""}${userSentryOptions.tunnelRoute}` : void 0, // The webpack plugin's release injection breaks the `app` directory so we inject the release manually here instead. // Having a release defined in dev-mode spams releases in Sentry so we only set one in non-dev mode // Only inject if release creation is not explicitly disabled (to maintain build determinism) SENTRY_RELEASE: releaseToInject && !buildContext.dev ? { id: releaseToInject } : void 0, _sentryBasePath: buildContext.dev ? userNextConfig.basePath : void 0, // This is used to determine version-based dev-symbolication behavior _sentryNextJsVersion: nextJsVersion }; const serverValues = { ...isomorphicValues, // Make sure that if we have a windows path, the backslashes are interpreted as such (rather than as escape // characters) _sentryRewriteFramesDistDir: userNextConfig.distDir?.replace(/\\/g, "\\\\") || ".next", // Inject Vercel crons config for server-side cron auto-instrumentation _sentryVercelCronsConfig: vercelCronsConfig ? JSON.stringify(vercelCronsConfig) : void 0 }; const clientValues = { ...isomorphicValues, // Get the path part of `assetPrefix`, minus any trailing slash. (We use a placeholder for the origin if // `assetPrefix` doesn't include one. Since we only care about the path, it doesn't matter what it is.) _sentryRewriteFramesAssetPrefixPath: assetPrefix ? new URL(assetPrefix, "http://dogs.are.great").pathname.replace(/\/$/, "") : "", _sentryAssetPrefix: userNextConfig.assetPrefix, _sentryExperimentalThirdPartyOriginStackFrames: userSentryOptions._experimental?.thirdPartyOriginStackFrames ? "true" : void 0, _sentryRouteManifest: JSON.stringify(routeManifest) }; if (buildContext.isServer) { newConfig.module.rules.push({ // TODO: Find a more bulletproof way of matching. For now this is fine and doesn't hurt anyone. It merely sets some globals. test: /(src[\\/])?instrumentation.(js|ts)/, use: [ { loader: path.resolve(__dirname, "loaders/valueInjectionLoader.js"), options: { values: serverValues } } ] }); } else { newConfig.module.rules.push({ test: /(?:sentry\.client\.config\.(jsx?|tsx?)|(?:src[\\/])?instrumentation-client\.(js|ts))$/, use: [ { loader: path.resolve(__dirname, "loaders/valueInjectionLoader.js"), options: { values: clientValues } } ] }); } } function resolveNextPackageDirFromDirectory(basedir) { try { return path.dirname(module$1.createRequire(`${basedir}/`).resolve("next/package.json")); } catch { return void 0; } } const POTENTIAL_REQUEST_ASYNC_STORAGE_LOCATIONS = [ // Original location of RequestAsyncStorage // https://github.com/vercel/next.js/blob/46151dd68b417e7850146d00354f89930d10b43b/packages/next/src/client/components/request-async-storage.ts "next/dist/client/components/request-async-storage.js", // Introduced in Next.js 13.4.20 // https://github.com/vercel/next.js/blob/e1bc270830f2fc2df3542d4ef4c61b916c802df3/packages/next/src/client/components/request-async-storage.external.ts "next/dist/client/components/request-async-storage.external.js", // Introduced in Next.js 15.0.0-canary.180 // https://github.com/vercel/next.js/blob/541167b9b0fed6af9f36472e632863ffec41f18c/packages/next/src/server/app-render/work-unit-async-storage.external.ts "next/dist/server/app-render/work-unit-async-storage.external.js", // Introduced in Next.js 15.0.0-canary.182 // https://github.com/vercel/next.js/blob/f35159e5e80138ca7373f57b47edcaae3bcf1728/packages/next/src/client/components/work-unit-async-storage.external.ts "next/dist/client/components/work-unit-async-storage.external.js" ]; function getRequestAsyncStorageModuleLocation(webpackContextDir, webpackResolvableModuleLocations) { if (webpackResolvableModuleLocations === void 0) { return void 0; } const absoluteWebpackResolvableModuleLocations = webpackResolvableModuleLocations.map( (loc) => path.resolve(webpackContextDir, loc) ); for (const webpackResolvableLocation of absoluteWebpackResolvableModuleLocations) { const nextPackageDir = resolveNextPackageDirFromDirectory(webpackResolvableLocation); if (nextPackageDir) { const asyncLocalStorageLocation = POTENTIAL_REQUEST_ASYNC_STORAGE_LOCATIONS.find( (loc) => fs.existsSync(path.join(nextPackageDir, "..", loc)) ); if (asyncLocalStorageLocation) { return asyncLocalStorageLocation; } } } return void 0; } function addOtelWarningIgnoreRule(newConfig) { const ignoreRules = [ // Inspired by @matmannion: https://github.com/getsentry/sentry-javascript/issues/12077#issuecomment-2180307072 (warning, compilation) => { try { if (!warning.module) { return false; } const isDependencyThatMayRaiseCriticalDependencyMessage = /@opentelemetry\/instrumentation/.test(warning.module.readableIdentifier(compilation.requestShortener)) || /@prisma\/instrumentation/.test(warning.module.readableIdentifier(compilation.requestShortener)); const isCriticalDependencyMessage = /Critical dependency/.test(warning.message); return isDependencyThatMayRaiseCriticalDependencyMessage && isCriticalDependencyMessage; } catch { return false; } }, // We provide these objects in addition to the hook above to provide redundancy in case the hook fails. { module: /@opentelemetry\/instrumentation/, message: /Critical dependency/ }, { module: /@prisma\/instrumentation/, message: /Critical dependency/ }, { module: /require-in-the-middle/, message: /Critical dependency/ } ]; if (newConfig.ignoreWarnings === void 0) { newConfig.ignoreWarnings = ignoreRules; } else if (Array.isArray(newConfig.ignoreWarnings)) { newConfig.ignoreWarnings.push(...ignoreRules); } } function addEdgeRuntimePolyfills(newConfig, buildContext) { newConfig.plugins = newConfig.plugins || []; newConfig.plugins.push( new buildContext.webpack.ProvidePlugin({ performance: [path.resolve(__dirname, "polyfills", "perf_hooks.js"), "performance"] }) ); newConfig.resolve = newConfig.resolve || {}; newConfig.resolve.alias = { ...newConfig.resolve.alias, // Redirect perf_hooks imports to a polyfilled version perf_hooks: path.resolve(__dirname, "polyfills", "perf_hooks.js") }; } function setupTreeshakingFromConfig(userSentryOptions, newConfig, buildContext) { const defines = {}; newConfig.plugins = newConfig.plugins || []; if (userSentryOptions.webpack?.treeshake?.removeDebugLogging) { defines.__SENTRY_DEBUG__ = false; } if (userSentryOptions.webpack?.treeshake?.removeTracing) { defines.__SENTRY_TRACING__ = false; } if (userSentryOptions.webpack?.treeshake?.excludeReplayIframe) { defines.__RRWEB_EXCLUDE_IFRAME__ = true; } if (userSentryOptions.webpack?.treeshake?.excludeReplayShadowDOM) { defines.__RRWEB_EXCLUDE_SHADOW_DOM__ = true; } if (userSentryOptions.webpack?.treeshake?.excludeReplayCompressionWorker) { defines.__SENTRY_EXCLUDE_REPLAY_WORKER__ = true; } if (Object.keys(defines).length > 0) { newConfig.plugins.push(new buildContext.webpack.DefinePlugin(defines)); } } exports.constructWebpackConfigFunction = constructWebpackConfigFunction; //# sourceMappingURL=webpack.js.map