1 | import { resolve, posix } from 'path'
|
2 | import consola from 'consola'
|
3 | import deepMerge from 'deepmerge'
|
4 | import * as Sentry from '@sentry/node'
|
5 | import * as Integrations from '@sentry/integrations'
|
6 | import WebpackPlugin from '@sentry/webpack-plugin'
|
7 |
|
8 | const logger = consola.withScope('nuxt:sentry')
|
9 |
|
10 | const filterDisabledIntegration = integrations => Object.keys(integrations)
|
11 | .filter(key => integrations[key])
|
12 |
|
13 | export default function SentryModule (moduleOptions) {
|
14 | const defaults = {
|
15 | dsn: process.env.SENTRY_DSN || false,
|
16 | disabled: process.env.SENTRY_DISABLED || false,
|
17 | initialize: process.env.SENTRY_INITIALIZE || true,
|
18 | disableClientSide: process.env.SENTRY_DISABLE_CLIENT_SIDE || false,
|
19 | disableServerSide: process.env.SENTRY_DISABLE_SERVER_SIDE || false,
|
20 | publishRelease: process.env.SENTRY_PUBLISH_RELEASE || false,
|
21 | disableServerRelease: process.env.SENTRY_DISABLE_SERVER_RELEASE || false,
|
22 | disableClientRelease: process.env.SENTRY_DISABLE_CLIENT_RELEASE || false,
|
23 | attachCommits: process.env.SENTRY_AUTO_ATTACH_COMMITS || false,
|
24 | sourceMapStyle: 'source-map',
|
25 | repo: process.env.SENTRY_RELEASE_REPO || false,
|
26 | clientIntegrations: {
|
27 | Dedupe: {},
|
28 | ExtraErrorData: {},
|
29 | ReportingObserver: {},
|
30 | RewriteFrames: {},
|
31 | Vue: { attachProps: true }
|
32 | },
|
33 | serverIntegrations: {
|
34 | Dedupe: {},
|
35 | ExtraErrorData: {},
|
36 | RewriteFrames: {},
|
37 | Transaction: {}
|
38 | },
|
39 | config: {
|
40 | environment: this.options.dev ? 'development' : 'production'
|
41 | },
|
42 | serverConfig: {},
|
43 | clientConfig: {},
|
44 | webpackConfig: {
|
45 | include: [],
|
46 | ignore: [
|
47 | 'node_modules',
|
48 | '.nuxt/dist/client/img'
|
49 | ],
|
50 | configFile: '.sentryclirc'
|
51 | }
|
52 | }
|
53 |
|
54 | const topLevelOptions = this.options.sentry || {}
|
55 | const options = deepMerge.all([defaults, topLevelOptions, moduleOptions])
|
56 |
|
57 | options.serverConfig = deepMerge.all([options.config, options.serverConfig])
|
58 | options.clientConfig = deepMerge.all([options.config, options.clientConfig])
|
59 |
|
60 | if (options.publishRelease) {
|
61 | if (!options.webpackConfig.urlPrefix) {
|
62 |
|
63 |
|
64 | const publicPath = posix.join(this.options.router.base, this.options.build.publicPath)
|
65 | options.webpackConfig.urlPrefix = publicPath.startsWith('/') ? `~${publicPath}` : publicPath
|
66 | }
|
67 |
|
68 | if (typeof options.webpackConfig.include === 'string') {
|
69 | options.webpackConfig.include = [options.webpackConfig.include]
|
70 | }
|
71 |
|
72 | const { buildDir } = this.options
|
73 |
|
74 | if (!options.disableServerRelease) {
|
75 | options.webpackConfig.include.push(`${buildDir}/dist/server`)
|
76 | }
|
77 | if (!options.disableClientRelease) {
|
78 | options.webpackConfig.include.push(`${buildDir}/dist/client`)
|
79 | }
|
80 |
|
81 | if (options.config.release && !options.webpackConfig.release) {
|
82 | options.webpackConfig.release = options.config.release
|
83 | }
|
84 |
|
85 | if (options.attachCommits) {
|
86 | options.webpackConfig.setCommits = {
|
87 | auto: true
|
88 | }
|
89 |
|
90 | if (options.repo) {
|
91 | options.webpackConfig.setCommits.repo = options.repo
|
92 | }
|
93 | }
|
94 | }
|
95 |
|
96 | const initializationRequired = options.initialize && options.dsn
|
97 |
|
98 | if (!options.dsn) {
|
99 | logger.info('Errors will not be logged because no DSN has been provided')
|
100 | }
|
101 |
|
102 |
|
103 | if (!options.disabled && !options.disableClientSide) {
|
104 | this.addPlugin({
|
105 | src: resolve(__dirname, 'sentry.client.js'),
|
106 | fileName: 'sentry.client.js',
|
107 | mode: 'client',
|
108 | options: {
|
109 | config: {
|
110 | dsn: options.dsn,
|
111 | ...options.clientConfig
|
112 | },
|
113 | initialize: initializationRequired,
|
114 | integrations: filterDisabledIntegration(options.clientIntegrations)
|
115 | .reduce((res, key) => {
|
116 | res[key] = options.clientIntegrations[key]
|
117 | return res
|
118 | }, {})
|
119 | }
|
120 | })
|
121 | } else {
|
122 | logger.info('Sentry client side errors will not be logged because the disable option has been set')
|
123 | this.addPlugin({
|
124 | src: resolve(__dirname, 'sentry.mocked.js'),
|
125 | fileName: 'sentry.client.js',
|
126 | mode: 'client'
|
127 | })
|
128 | }
|
129 |
|
130 |
|
131 | if (!options.disabled && !options.disableServerSide) {
|
132 |
|
133 | if (initializationRequired) {
|
134 | Sentry.init({
|
135 | dsn: options.dsn,
|
136 | ...options.serverConfig,
|
137 | integrations: filterDisabledIntegration(options.serverIntegrations)
|
138 | .map(name => new Integrations[name](options.serverIntegrations[name]))
|
139 | })
|
140 | }
|
141 |
|
142 | process.sentry = Sentry
|
143 | logger.success('Started logging errors to Sentry')
|
144 |
|
145 | this.addPlugin({
|
146 | src: resolve(__dirname, 'sentry.server.js'),
|
147 | fileName: 'sentry.server.js',
|
148 | mode: 'server'
|
149 | })
|
150 | this.nuxt.hook('render:setupMiddleware', app => app.use(Sentry.Handlers.requestHandler()))
|
151 | this.nuxt.hook('render:errorMiddleware', app => app.use(Sentry.Handlers.errorHandler()))
|
152 | this.nuxt.hook('generate:routeFailed', ({ route, errors }) => {
|
153 | errors.forEach(({ error }) => Sentry.withScope((scope) => {
|
154 | scope.setExtra('route', route)
|
155 | Sentry.captureException(error)
|
156 | }))
|
157 | })
|
158 | } else {
|
159 | logger.info('Sentry server side errors will not be logged because the disable option has been set')
|
160 | this.addPlugin({
|
161 | src: resolve(__dirname, 'sentry.mocked.js'),
|
162 | fileName: 'sentry.server.js',
|
163 | mode: 'server'
|
164 | })
|
165 | }
|
166 |
|
167 |
|
168 | if (options.publishRelease && !options.disabled && !this.options.dev) {
|
169 | this.nuxt.hook('webpack:config', (webpackConfigs) => {
|
170 | for (const config of webpackConfigs) {
|
171 | config.devtool = options.sourceMapStyle
|
172 | }
|
173 |
|
174 |
|
175 |
|
176 | const config = webpackConfigs[webpackConfigs.length - 1]
|
177 |
|
178 | config.plugins = config.plugins || []
|
179 | config.plugins.push(new WebpackPlugin(options.webpackConfig))
|
180 |
|
181 | logger.info('Enabling uploading of release sourcemaps to Sentry')
|
182 | })
|
183 | }
|
184 | }
|