UNPKG

@nuxtjs/sentry

Version:
820 lines (813 loc) 29.1 kB
import { fileURLToPath } from 'node:url'; import { defu } from 'defu'; import { resolvePath } from 'mlly'; import * as Sentry from '@sentry/node'; import { autoDiscoverNodePerformanceMonitoringIntegrations, Integrations, withScope, captureException } from '@sentry/node'; import { existsSync } from 'node:fs'; import { consola } from 'consola'; import hash from 'hash-sum'; import { parse, basename, resolve, normalize, relative } from 'pathe'; import { resolveAlias as resolveAlias$1 } from 'pathe/utils'; import { fileURLToPath as fileURLToPath$1 } from 'url'; import { resolve as resolve$1 } from 'path'; import initJiti from 'jiti'; import * as SentryCore from '@sentry/core'; import * as PluggableIntegrations from '@sentry/integrations'; const nuxtCtx = { value: null }; function useNuxt() { const instance = nuxtCtx.value; if (!instance) { throw new Error("Nuxt instance is unavailable!"); } return instance; } function tryUseNuxt() { return nuxtCtx.value; } const NUXT2_SHIMS_KEY = "__nuxt2_shims_sentry_key__"; function nuxt2Shims(nuxt) { if (nuxt[NUXT2_SHIMS_KEY]) { return; } nuxt[NUXT2_SHIMS_KEY] = true; nuxt.hooks = nuxt; if (!nuxtCtx.value) { nuxtCtx.value = nuxt; nuxt.hook("close", () => { nuxtCtx.value = null; }); } } function defineNuxtModule(definition) { if (!definition.meta) { definition.meta = {}; } if (definition.meta.configKey === void 0) { definition.meta.configKey = definition.meta.name; } function getOptions(inlineOptions) { const nuxt = useNuxt(); const configKey = definition.meta.configKey || definition.meta.name; const _defaults = definition.defaults instanceof Function ? definition.defaults(nuxt) : definition.defaults; const _options = defu(inlineOptions, nuxt.options[configKey], _defaults); return Promise.resolve(_options); } async function normalizedModule(inlineOptions) { const nuxt = this.nuxt; const uniqueKey = definition.meta.name || definition.meta.configKey; if (uniqueKey) { nuxt.options._requiredModules = nuxt.options._requiredModules || {}; if (nuxt.options._requiredModules[uniqueKey]) { return false; } nuxt.options._requiredModules[uniqueKey] = true; } nuxt2Shims(nuxt); const _options = await getOptions(inlineOptions); const res = await definition.setup?.call(null, _options, nuxt) ?? {}; return defu(res, {}); } normalizedModule.getMeta = () => Promise.resolve(definition.meta); normalizedModule.getOptions = getOptions; return normalizedModule; } const logger$1 = consola; function useLogger(tag) { return tag ? logger$1.withTag(tag) : logger$1; } function resolveAlias(path, alias) { if (!alias) { alias = tryUseNuxt()?.options.alias || {}; } return resolveAlias$1(path, alias); } function normalizePlugin(plugin) { if (typeof plugin === "string") { plugin = { src: plugin }; } else { plugin = { ...plugin }; } if (!plugin.src) { throw new Error("Invalid plugin. src option is required: " + JSON.stringify(plugin)); } const nonTopLevelPlugin = plugin.src.match(/\/plugins\/[^/]+\/index\.[^/]+$/i); if (nonTopLevelPlugin && nonTopLevelPlugin.length > 0 && !useNuxt().options.plugins.find((i) => (typeof i === "string" ? i : i.src).endsWith(nonTopLevelPlugin[0]))) { console.warn(`[warn] [nuxt] [deprecation] You are using a plugin that is within a subfolder of your plugins directory without adding it to your config explicitly. You can move it to the top-level plugins directory, or include the file '~${nonTopLevelPlugin[0]}' in your plugins config (https://nuxt.com/docs/api/configuration/nuxt-config#plugins-1) to remove this warning.`); } plugin.src = normalize(resolveAlias(plugin.src)); if (plugin.ssr) { plugin.mode = "server"; } if (!plugin.mode) { const [, mode = "all"] = plugin.src.match(/\.(server|client)(\.\w+)*$/) || []; plugin.mode = mode; } return plugin; } function addPlugin(_plugin, opts = {}) { const nuxt = useNuxt(); const plugin = normalizePlugin(_plugin); nuxt.options.plugins = nuxt.options.plugins.filter((p) => normalizePlugin(p).src !== plugin.src); nuxt.options.plugins[opts.append ? "push" : "unshift"](plugin); return plugin; } function addPluginTemplate(plugin, opts = {}) { const normalizedPlugin = typeof plugin === "string" ? { src: plugin } : { ...plugin, src: addTemplate(plugin).dst }; return addPlugin(normalizedPlugin, opts); } function addTemplate(_template) { const nuxt = useNuxt(); const template = normalizeTemplate(_template); nuxt.options.build.templates = nuxt.options.build.templates.filter((p) => normalizeTemplate(p).filename !== template.filename); nuxt.options.build.templates.push(template); return template; } function normalizeTemplate(template) { if (!template) { throw new Error("Invalid template: " + JSON.stringify(template)); } if (typeof template === "string") { template = { src: template }; } else { template = { ...template }; } if (template.src) { if (!existsSync(template.src)) { throw new Error("Template not found: " + template.src); } if (!template.filename) { const srcPath = parse(template.src); template.filename = template.fileName || `${basename(srcPath.dir)}.${srcPath.name}.${hash(template.src)}${srcPath.ext}`; } } if (!template.src && !template.getContents) { throw new Error("Invalid template. Either getContents or src options should be provided: " + JSON.stringify(template)); } if (!template.filename) { throw new Error("Invalid template. Either filename should be provided: " + JSON.stringify(template)); } if (template.filename.endsWith(".d.ts")) { template.write = true; } if (!template.dst) { const nuxt = useNuxt(); template.dst = resolve(nuxt.options.buildDir, template.filename); } return template; } function addWebpackPlugin(plugin, options) { extendWebpackConfig((config) => { config.plugins = config.plugins || []; if (Array.isArray(plugin)) { config.plugins.push(...plugin); } else { config.plugins.push(plugin); } }, options); } function extendWebpackConfig(fn, options = {}) { const nuxt = useNuxt(); if (options.dev === false && nuxt.options.dev) { return; } if (options.build === false && nuxt.options.build) { return; } nuxt.hook("webpack:config", (configs) => { if (options.server !== false) { const config = configs.find((i) => i.name === "server"); if (config) { fn(config); } } if (options.client !== false) { const config = configs.find((i) => i.name === "client"); if (config) { fn(config); } } }); } const boolToText = (value) => value ? "enabled" : "disabled"; const envToBool = (env) => Boolean(env && env.toLowerCase() !== "false" && env !== "0"); const canInitialize = (options) => Boolean(options.initialize && options.dsn); const clientSentryEnabled = (options) => !options.disabled && !options.disableClientSide; const serverSentryEnabled = (options) => !options.disabled && !options.disableServerSide; function callOnce(fn) { let called = false; return function callOnceWrapper(...subargs) { if (!called) { called = true; return fn(...subargs); } }; } const jiti = initJiti(fileURLToPath(import.meta.url)); const BROWSER_CORE_INTEGRATIONS = { FunctionToString: true, InboundFilters: true, LinkedErrors: true }; const BROWSER_INTEGRATIONS = { Breadcrumbs: true, GlobalHandlers: true, HttpContext: true, Replay: true, TryCatch: true }; const BROWSER_PLUGGABLE_INTEGRATIONS = { CaptureConsole: true, ContextLines: true, Debug: true, Dedupe: true, ExtraErrorData: true, HttpClient: true, ReportingObserver: true, RewriteFrames: true, SessionTiming: true }; const SERVER_CORE_INTEGRATIONS = { FunctionToString: true, InboundFilters: true, LinkedErrors: true, RequestData: true }; const SERVER_NODE_INTEGRATIONS = { Anr: true, Apollo: true, Console: true, Context: true, ContextLines: true, Express: true, GraphQL: true, Hapi: true, Http: true, LocalVariables: true, Modules: true, Mongo: true, Mysql: true, OnUncaughtException: true, OnUnhandledRejection: true, Postgres: true, Prisma: true, Spotlight: true, Undici: true }; const SERVER_PLUGGABLE_INTEGRATIONS = { CaptureConsole: true, Debug: true, Dedupe: true, ExtraErrorData: true, HttpClient: true, ReportingObserver: true, RewriteFrames: true, SessionTiming: true }; const INTEGRATION_TO_IMPORT_NAME_MAP = { Anr: "Anr", Apollo: "Apollo", Breadcrumbs: "breadcrumbsIntegration", CaptureConsole: "captureConsoleIntegration", Console: "Console", Context: "Context", ContextLines: "contextLinesIntegration", Debug: "debugIntegration", Dedupe: "dedupeIntegration", Express: "Express", ExtraErrorData: "extraErrorDataIntegration", FunctionToString: "functionToStringIntegration", GlobalHandlers: "globalHandlersIntegration", GraphQL: "GraphQL", Hapi: "Hapi", Http: "Http", HttpClient: "httpClientIntegration", HttpContext: "httpContextIntegration", InboundFilters: "inboundFiltersIntegration", LinkedErrors: "linkedErrorsIntegration", LocalVariables: "LocalVariables", Modules: "Modules", Mongo: "Mongo", Mysql: "Mysql", OnUncaughtException: "OnUncaughtException", OnUnhandledRejection: "OnUnhandledRejection", Postgres: "Postgres", Prisma: "Prisma", ProfilingIntegration: "ProfilingIntegration", Replay: "replayIntegration", ReportingObserver: "reportingObserverIntegration", RequestData: "requestDataIntegration", RewriteFrames: "rewriteFramesIntegration", SessionTiming: "sessionTimingIntegration", Spotlight: "Spotlight", TryCatch: "browserApiErrorsIntegration", Undici: "Undici" }; function mapClientIntegrationToImportName(key) { return INTEGRATION_TO_IMPORT_NAME_MAP[key]; } function mapServerIntegrationToImportName(key) { if (key === "ContextLines") { return "ContextLines"; } return INTEGRATION_TO_IMPORT_NAME_MAP[key]; } const SERVER_PROFILING_INTEGRATION = "ProfilingIntegration"; function getEnabledIntegrations(integrations) { return getIntegrationsKeys(integrations).filter((key) => integrations[key]); } function getDisabledIntegrationKeys(integrations) { return getIntegrationsKeys(integrations).filter((key) => integrations[key] === false); } function getIntegrationsKeys(integrations) { return Object.keys(integrations); } function isBrowserCoreIntegration(name) { return name in BROWSER_CORE_INTEGRATIONS; } function isBrowserDefaultIntegration(name) { return name in BROWSER_INTEGRATIONS; } function isBrowserPluggableIntegration(name) { return name in BROWSER_PLUGGABLE_INTEGRATIONS; } function isServerCoreIntegration(name) { return name in SERVER_CORE_INTEGRATIONS; } function isServerNodeIntegration(name) { return name in SERVER_NODE_INTEGRATIONS; } function isServerPlugabbleIntegration(name) { return name in SERVER_PLUGGABLE_INTEGRATIONS; } async function getApiMethods(packageName) { const packageApi = await import(packageName); const apiMethods = []; for (const key in packageApi) { if (key === "default") { for (const subKey in packageApi[key]) { if (typeof packageApi[key][subKey] === "function") { apiMethods.push(subKey); } } continue; } if (typeof packageApi[key] === "function") { apiMethods.push(key); } } return apiMethods; } async function resolveRelease(moduleOptions) { if (!("release" in moduleOptions.config)) { try { const SentryCli = await import('@sentry/cli').then((m) => m.default || m); const cli = new SentryCli(); return (await cli.releases.proposeVersion()).trim(); } catch { } } } function resolveClientLazyOptions(options, apiMethods, logger) { if (!options.lazy) { return; } const defaultLazyOptions = { injectMock: true, injectLoadHook: false, mockApiMethods: true, chunkName: "sentry", webpackPrefetch: false, webpackPreload: false }; options.lazy = defu(options.lazy, defaultLazyOptions); if (!options.lazy.injectMock) { options.lazy.mockApiMethods = []; } else if (options.lazy.mockApiMethods === true) { options.lazy.mockApiMethods = apiMethods; } else if (Array.isArray(options.lazy.mockApiMethods)) { const mockMethods = options.lazy.mockApiMethods; options.lazy.mockApiMethods = mockMethods.filter((method) => apiMethods.includes(method)); const notfoundMethods = mockMethods.filter((method) => !apiMethods.includes(method)); if (notfoundMethods.length) { logger.warn("Some specified methods to mock weren't found in @sentry/vue:", notfoundMethods); } if (!options.lazy.mockApiMethods.includes("captureException")) { options.lazy.mockApiMethods.push("captureException"); } } } function resolveTracingOptions(options) { if (!options.tracing) { return; } const defaultTracingOptions = { tracesSampleRate: 1, browserTracing: {}, vueOptions: { trackComponents: true }, vueRouterInstrumentationOptions: { routeLabel: "name" } }; options.tracing = defu(options.tracing, defaultTracingOptions); if (options.config.tracesSampleRate === void 0) { options.config.tracesSampleRate = options.tracing.tracesSampleRate; } } async function resolveClientOptions(nuxt, moduleOptions, logger) { const options = defu(moduleOptions); let clientConfigPath; if (typeof options.clientConfig === "string") { clientConfigPath = resolveAlias(options.clientConfig); clientConfigPath = relative(nuxt.options.buildDir, clientConfigPath); } else { options.config = defu(options.clientConfig, options.config); } const apiMethods = await getApiMethods("@sentry/vue"); resolveClientLazyOptions(options, apiMethods, logger); resolveTracingOptions(options); for (const name of getIntegrationsKeys(options.clientIntegrations)) { if (!isBrowserDefaultIntegration(name) && !isBrowserCoreIntegration(name) && !isBrowserPluggableIntegration(name)) { logger.warn(`Sentry clientIntegration "${name}" is not recognized and will be ignored.`); delete options.clientIntegrations[name]; } } let customClientIntegrations; if (options.customClientIntegrations) { if (typeof options.customClientIntegrations === "string") { customClientIntegrations = resolveAlias(options.customClientIntegrations); customClientIntegrations = relative(nuxt.options.buildDir, customClientIntegrations); } else { logger.warn(`Invalid customClientIntegrations option. Expected a file path, got "${typeof options.customClientIntegrations}".`); } } const importsBrowser = []; const importsCore = []; const importsPluggable = []; const integrations = getEnabledIntegrations(options.clientIntegrations).reduce((res, key) => { const importName = mapClientIntegrationToImportName(key); if (key in BROWSER_INTEGRATIONS) { importsBrowser.push(importName); } else if (key in BROWSER_CORE_INTEGRATIONS) { importsCore.push(importName); } else if (key in BROWSER_PLUGGABLE_INTEGRATIONS) { importsPluggable.push(importName); } res[importName] = options.clientIntegrations[key]; return res; }, {}); const imports = { "~@sentry/browser": importsBrowser, "~@sentry/core": importsCore, "~@sentry/integrations": importsPluggable, "~@sentry/vue": ["init", ...options.tracing ? ["browserTracingIntegration"] : []] }; return { dev: nuxt.options.dev, runtimeConfigKey: options.runtimeConfigKey, config: { dsn: options.dsn, ...options.config }, clientConfigPath, DISABLED_INTEGRATION_KEYS: getDisabledIntegrationKeys(options.clientIntegrations), lazy: options.lazy, apiMethods, customClientIntegrations, logMockCalls: options.logMockCalls, // for mocked only tracing: options.tracing, imports, initialize: canInitialize(options), integrations }; } async function resolveServerOptions(nuxt, moduleOptions, logger) { const options = defu(moduleOptions); if (options.tracing) { resolveTracingOptions(options); options.serverIntegrations = defu(options.serverIntegrations, { Http: { tracing: true } }); } if (typeof options.serverConfig === "string") { const resolvedPath = resolveAlias(options.serverConfig); try { const mod = jiti(resolvedPath); options.serverConfig = (mod.default || mod)(); } catch (error) { logger.error(`Error handling the serverConfig plugin: ${error}`); } } options.config = defu(getServerRuntimeConfig(nuxt, options), options.serverConfig, options.config); for (const name of getIntegrationsKeys(options.serverIntegrations)) { if (!isServerNodeIntegration(name) && !isServerCoreIntegration(name) && !isServerPlugabbleIntegration(name) && name !== SERVER_PROFILING_INTEGRATION) { logger.warn(`Sentry serverIntegration "${name}" is not recognized and will be ignored.`); delete options.serverIntegrations[name]; } } let customIntegrations = []; if (options.customServerIntegrations) { const resolvedPath = resolveAlias(options.customServerIntegrations); try { const mod = jiti(resolvedPath); customIntegrations = (mod.default || mod)(); if (!Array.isArray(customIntegrations)) { logger.error(`Invalid value returned from customServerIntegrations plugin. Expected an array, got "${typeof customIntegrations}".`); } } catch (error) { logger.error(`Error handling the customServerIntegrations plugin: ${error}`); } } if (SERVER_PROFILING_INTEGRATION in options.serverIntegrations) { const enabled = options.serverIntegrations[SERVER_PROFILING_INTEGRATION]; delete options.serverIntegrations[SERVER_PROFILING_INTEGRATION]; if (enabled) { try { const { ProfilingIntegration } = await import('@sentry/profiling-node').then((m) => m.default || m); customIntegrations.push(new ProfilingIntegration()); } catch (error) { logger.error(`To use the ${SERVER_PROFILING_INTEGRATION} integration you need to install the "@sentry/profiling-node" dependency.`); throw new Error(error.message); } } } const resolvedIntegrations = [ // Automatically instrument Node.js libraries and frameworks ...options.tracing ? autoDiscoverNodePerformanceMonitoringIntegrations() : [], ...getEnabledIntegrations(options.serverIntegrations).map((name) => { const importName = mapServerIntegrationToImportName(name); const opt = options.serverIntegrations[name]; try { if (isServerCoreIntegration(name)) { return Object.keys(opt).length ? SentryCore[importName](opt) : SentryCore[importName](); } else if (isServerNodeIntegration(name)) { return Object.keys(opt).length ? new Integrations[name](opt) : new Integrations[name](); } else if (isServerPlugabbleIntegration(name)) { return Object.keys(opt).length ? PluggableIntegrations[importName](opt) : PluggableIntegrations[importName](); } else { throw new Error(`Unsupported server integration "${name}"`); } } catch (error) { throw new Error(`Failed initializing server integration "${name}". ${error}`); } }), ...customIntegrations ]; const disabledIntegrationKeys = getDisabledIntegrationKeys(options.serverIntegrations); options.config.integrations = (defaultIntegrations) => { return [ ...defaultIntegrations.filter((integration) => !disabledIntegrationKeys.includes(integration.name)), ...resolvedIntegrations ]; }; return { config: { dsn: options.dsn, ...options.config }, apiMethods: await getApiMethods("@sentry/node"), lazy: options.lazy, logMockCalls: options.logMockCalls, // for mocked only tracing: options.tracing }; } function getServerRuntimeConfig(nuxt, options) { const { publicRuntimeConfig } = nuxt.options; const { runtimeConfigKey } = options; if (publicRuntimeConfig && typeof publicRuntimeConfig !== "function" && runtimeConfigKey in publicRuntimeConfig) { return defu( publicRuntimeConfig[runtimeConfigKey].serverConfig, publicRuntimeConfig[runtimeConfigKey].config ); } } const RESOLVED_RELEASE_FILENAME = "sentry.release.config.mjs"; async function buildHook(nuxt, moduleOptions, logger) { const release = await resolveRelease(moduleOptions); const templateDir = fileURLToPath$1(new URL("./templates", import.meta.url)); const pluginOptionClient = clientSentryEnabled(moduleOptions) && canInitialize(moduleOptions) ? moduleOptions.lazy ? "lazy" : "client" : "mocked"; const clientOptions = defu({ config: { release } }, await resolveClientOptions(nuxt, moduleOptions, logger)); addPluginTemplate({ src: resolve$1(templateDir, `plugin.${pluginOptionClient}.js`), filename: "sentry.client.js", mode: "client", options: clientOptions }); if (pluginOptionClient !== "mocked") { addTemplate({ src: resolve$1(templateDir, "client.shared.js"), filename: "sentry.client.shared.js", options: clientOptions }); } const pluginOptionServer = serverSentryEnabled(moduleOptions) ? "server" : "mocked"; const serverOptions = defu({ config: { release } }, await resolveServerOptions(nuxt, moduleOptions, logger)); addPluginTemplate({ src: resolve$1(templateDir, `plugin.${pluginOptionServer}.js`), filename: "sentry.server.js", mode: "server", options: serverOptions }); if (serverSentryEnabled(moduleOptions)) { addTemplate({ src: resolve$1(templateDir, "options.ejs"), filename: RESOLVED_RELEASE_FILENAME, options: { release } }); } if (!clientOptions.dev && !clientOptions.config.debug) { const webpack = await import('webpack').then((m) => m.default || m); addWebpackPlugin(new webpack.DefinePlugin({ __SENTRY_DEBUG__: "false" })); } } async function webpackConfigHook(nuxt, webpackConfigs, options, logger) { let WebpackPlugin; try { WebpackPlugin = await import('@sentry/webpack-plugin').then((m) => m.default || m); } catch { throw new Error('The "@sentry/webpack-plugin" package must be installed as a dev dependency to use the "publishRelease" option.'); } const publishRelease = defu(options.publishRelease); if (!publishRelease.sourcemaps) { publishRelease.sourcemaps = {}; } if (!publishRelease.sourcemaps.ignore) { publishRelease.sourcemaps.ignore = []; } if (!Array.isArray(publishRelease.sourcemaps.ignore)) { publishRelease.sourcemaps.ignore = [publishRelease.sourcemaps.ignore]; } if (!publishRelease.release) { publishRelease.release = {}; } publishRelease.release.name = publishRelease.release.name || options.config.release || await resolveRelease(options); if (!publishRelease.release.name) { logger.warn('Sentry release will not be published because "config.release" or "publishRelease.release.name" was not set nor it was possible to determine it automatically from the repository.'); return; } for (const config of webpackConfigs) { config.devtool = options.sourceMapStyle; config.plugins = config.plugins || []; config.plugins.push(WebpackPlugin.sentryWebpackPlugin(publishRelease)); } } async function initializeServerSentry(nuxt, moduleOptions, sentryHandlerProxy, logger) { if (process.sentry) { return; } let release; try { const path = resolve$1(nuxt.options.buildDir, RESOLVED_RELEASE_FILENAME); release = (await import(path)).release; } catch { } const serverOptions = await resolveServerOptions(nuxt, moduleOptions, logger); const config = defu({ release }, serverOptions.config); process.sentry = Sentry; if (canInitialize(moduleOptions)) { Sentry.init(config); sentryHandlerProxy.errorHandler = Sentry.Handlers.errorHandler(); sentryHandlerProxy.requestHandler = Sentry.Handlers.requestHandler(moduleOptions.requestHandlerConfig); if (serverOptions.tracing) { sentryHandlerProxy.tracingHandler = Sentry.Handlers.tracingHandler(); } } } async function shutdownServerSentry() { if (process.sentry) { await process.sentry.close(); process.sentry = void 0; } } const logger = useLogger("nuxt:sentry"); const moduleDir = fileURLToPath(new URL("./", import.meta.url)); const module = defineNuxtModule({ meta: { name: "@nuxtjs/sentry", configKey: "sentry" }, defaults: (nuxt) => ({ lazy: false, dsn: process.env.SENTRY_DSN || "", disabled: envToBool(process.env.SENTRY_DISABLED) || false, initialize: envToBool(process.env.SENTRY_INITIALIZE) || true, runtimeConfigKey: "sentry", disableClientSide: envToBool(process.env.SENTRY_DISABLE_CLIENT_SIDE) || false, disableServerSide: envToBool(process.env.SENTRY_DISABLE_SERVER_SIDE) || false, publishRelease: envToBool(process.env.SENTRY_PUBLISH_RELEASE) || false, disableServerRelease: envToBool(process.env.SENTRY_DISABLE_SERVER_RELEASE) || false, disableClientRelease: envToBool(process.env.SENTRY_DISABLE_CLIENT_RELEASE) || false, logMockCalls: true, sourceMapStyle: "hidden-source-map", tracing: false, clientIntegrations: { ExtraErrorData: {}, ReportingObserver: { types: ["crash"] } }, serverIntegrations: { Dedupe: {}, ExtraErrorData: {}, RewriteFrames: { root: nuxt.options.rootDir } }, customClientIntegrations: "", customServerIntegrations: "", config: { environment: nuxt.options.dev ? "development" : "production" }, serverConfig: {}, clientConfig: {}, requestHandlerConfig: {} }), async setup(options, nuxt) { const defaultsPublishRelease = { sourcemaps: { ignore: [ "node_modules/**/*" ] } }; if (options.publishRelease) { options.publishRelease = defu(options.publishRelease, defaultsPublishRelease); } if (canInitialize(options) && (clientSentryEnabled(options) || serverSentryEnabled(options))) { const status = `(client side: ${boolToText(clientSentryEnabled(options))}, server side: ${boolToText(serverSentryEnabled(options))})`; logger.success(`Sentry reporting is enabled ${status}`); } else { let why; if (options.disabled) { why = '"disabled" option has been set'; } else if (!options.dsn) { why = "no DSN has been provided"; } else if (!options.initialize) { why = '"initialize" option has been set to false'; } else { why = "both client and server side clients are disabled"; } logger.info(`Sentry reporting is disabled (${why})`); } const aliasedDependencies = [ "lodash.mergewith", "@sentry/browser", "@sentry/core", "@sentry/integrations", "@sentry/utils", "@sentry/vue" ]; for (const dep of aliasedDependencies) { nuxt.options.alias[`~${dep}`] = (await resolvePath(dep, { url: moduleDir })).replace(/\/cjs\//, "/esm/"); } if (serverSentryEnabled(options)) { const sentryHandlerProxy = { errorHandler: (error, _, __, next) => { next(error); }, requestHandler: (_, __, next) => { next(); }, tracingHandler: (_, __, next) => { next(); } }; nuxt.hook("render:setupMiddleware", (app) => app.use((req, res, next) => { sentryHandlerProxy.requestHandler(req, res, next); })); if (options.tracing) { nuxt.hook("render:setupMiddleware", (app) => app.use((req, res, next) => { sentryHandlerProxy.tracingHandler(req, res, next); })); } nuxt.hook("render:errorMiddleware", (app) => app.use((error, req, res, next) => { sentryHandlerProxy.errorHandler(error, req, res, next); })); nuxt.hook("generate:routeFailed", ({ route, errors }) => { errors.forEach(({ error }) => withScope((scope) => { scope.setExtra("route", route); captureException(error); })); }); { const isBuilding = nuxt.options._build && !nuxt.options.dev; const initHook = isBuilding ? "build:compile" : "ready"; nuxt.hook(initHook, () => initializeServerSentry(nuxt, options, sentryHandlerProxy, logger)); const shutdownHook = isBuilding ? "build:done" : "close"; const shutdownServerSentryOnce = callOnce(() => shutdownServerSentry()); nuxt.hook(shutdownHook, shutdownServerSentryOnce); } } nuxt.hook("build:before", () => buildHook(nuxt, options, logger)); if (options.publishRelease && !options.disabled && !nuxt.options.dev) { { nuxt.hook("webpack:config", (webpackConfigs) => webpackConfigHook(nuxt, webpackConfigs, options, logger)); } } } }); export { module as default };