Skip to content

Commit 82d4d92

Browse files
committed
fix: nuxt bridge support via nitro
1 parent 1b69f4a commit 82d4d92

File tree

7 files changed

+3368
-147
lines changed

7 files changed

+3368
-147
lines changed

lib/module.js

+104-80
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,120 @@
11
const path = require('path')
2-
const deepMerge = require('deepmerge')
2+
const { fileURLToPath } = require('url')
33
const AppInsights = require('applicationinsights')
4-
const logger = require('consola').withScope('nuxt:appInsights')
5-
const { requestHandler, errorHandler } = require('./serverHandlers')
6-
7-
module.exports = function appInsights (moduleOptions) {
8-
const defaults = {
9-
instrumentationKey: process.env.APPINSIGHTS_INSTRUMENTATION_KEY || false,
10-
serverConnectionString: process.env.APPINSIGHTS_CONNECTION_STRING || false,
11-
disabled: process.env.APPINSIGHTS_DISABLED || false,
12-
initialize: process.env.APPINSIGHTS_INITIALIZE || true,
13-
disableClientSide: process.env.APPINSIGHTS_DISABLE_CLIENT_SIDE || false,
14-
disableServerSide: process.env.APPINSIGHTS_DISABLE_SERVER_SIDE || false,
15-
trackPageView: process.env.APPINSIGHTS_TRACK_PAGE_VIEW || true,
16-
serverConfig: {},
17-
clientConfig: {}
18-
}
19-
const { nuxt } = this
4+
const deepMerge = require('deepmerge')
205

21-
const publicRuntimeConfig = (nuxt.options.publicRuntimeConfig || {}).appInsights || {}
6+
const { defineNuxtModule, getNuxtVersion, logger, addTemplate, addPluginTemplate, hasNuxtCompatibility } = require('@nuxt/kit')
7+
const { requestHandler, errorHandler } = require('./serverHandlers')
228

23-
const topLevelOptions = this.options.appInsights || {}
24-
const options = deepMerge.all([defaults, topLevelOptions, moduleOptions, publicRuntimeConfig])
9+
const DEFAULTS = {
10+
instrumentationKey: process.env.APPINSIGHTS_INSTRUMENTATION_KEY || false,
11+
serverConnectionString: process.env.APPINSIGHTS_CONNECTION_STRING || false,
12+
disabled: process.env.APPINSIGHTS_DISABLED || false,
13+
initialize: process.env.APPINSIGHTS_INITIALIZE || true,
14+
disableClientSide: process.env.APPINSIGHTS_DISABLE_CLIENT_SIDE || false,
15+
disableServerSide: process.env.APPINSIGHTS_DISABLE_SERVER_SIDE || false,
16+
trackPageView: process.env.APPINSIGHTS_TRACK_PAGE_VIEW || true,
17+
serverConfig: {},
18+
clientConfig: {}
19+
}
20+
module.exports = defineNuxtModule({
21+
meta: {
22+
name: '@nuxtjs/applicationinsights',
23+
configKey: 'appInsights',
24+
compatibility: {
25+
nuxt: '^2.0.0 || ^3.0.0-rc.7'
26+
}
27+
},
28+
defaults: DEFAULTS,
29+
async setup (_options, nuxt) {
30+
const options = deepMerge.all([_options, (nuxt.options.runtimeConfig ? nuxt.options.runtimeConfig.public.appInsights : (nuxt.options.publicRuntimeConfig || {}).appInsights) || {}, (nuxt.options.runtimeConfig ? nuxt.options.runtimeConfig.appInsights : (nuxt.options.privateRuntimeConfig || {}).appInsights) || {}])
31+
if (options.disabled) {
32+
logger.info('Errors will not be logged because the disable option has been set')
33+
return
34+
}
2535

26-
if (options.disabled) {
27-
logger.info('Errors will not be logged because the disable option has been set')
28-
return
29-
}
36+
if (!options.instrumentationKey) {
37+
logger.info('Errors will not be logged because no instrumentationKey has been provided')
38+
return
39+
}
3040

31-
if (!options.instrumentationKey) {
32-
logger.info('Errors will not be logged because no instrumentationKey has been provided')
33-
return
34-
}
41+
// Register the client plugin
42+
if (!options.disableClientSide) {
43+
addTemplate({
44+
src: path.resolve(__dirname, 'appinsights-vue.js'),
45+
fileName: 'appinsights-vue.js'
46+
})
47+
addPluginTemplate({
48+
src: path.resolve(__dirname, 'appinsights.client.js'),
49+
fileName: 'appinsights.client.js',
50+
mode: 'client',
51+
options: {
52+
config: {
53+
instrumentationKey: options.instrumentationKey,
54+
...options.clientConfig
55+
},
56+
initialize: options.initialize,
57+
trackPageView: options.trackPageView
58+
}
59+
})
60+
}
3561

36-
// Register the client plugin
37-
if (!options.disableClientSide) {
38-
this.addTemplate({
39-
src: path.resolve(__dirname, 'appinsights-vue.js'),
40-
fileName: 'appinsights-vue.js'
41-
})
42-
this.addPlugin({
43-
src: path.resolve(__dirname, 'appinsights.client.js'),
44-
fileName: 'appinsights.client.js',
45-
mode: 'client',
46-
options: {
47-
config: {
48-
instrumentationKey: options.instrumentationKey,
49-
...options.clientConfig
50-
},
51-
initialize: options.initialize,
52-
trackPageView: options.trackPageView
62+
// Register the server plugin
63+
if (!options.disableServerSide) {
64+
if (!options.serverConnectionString) {
65+
logger.info('Server errors will not be logged because no serverConnectionString provided')
66+
return
5367
}
54-
})
55-
}
5668

57-
// Register the server plugin
58-
if (!options.disableServerSide) {
59-
const privateRuntimeConfig = (nuxt.options.privateRuntimeConfig || {}).appInsights || {}
69+
if (await hasNuxtCompatibility({ bridge: true })) {
70+
addTemplate({
71+
src: path.resolve(__dirname, 'runtime/appinsights.init.js'),
72+
fileName: 'appinsights.init.js'
73+
})
74+
addPluginTemplate({
75+
src: path.resolve(__dirname, 'runtime/appinsights.server.js'),
76+
fileName: 'appinsights.server.js',
77+
mode: 'server',
78+
options
79+
})
80+
nuxt.hook('nitro:config', (config) => {
81+
// Add a nitro plugin that will run the validator for us on each request
82+
config.plugins = config.plugins || []
83+
config.plugins.push(fileURLToPath(new URL('./runtime/nitro', import.meta.url)))
84+
config.virtual = config.virtual || {}
85+
config.virtual['#appinsights-config'] = `export default ${JSON.stringify(options)}`
86+
config.virtual['#existing-error-handler'] = `import errorHandler from '${config.errorHandler}';export default errorHandler`
87+
config.errorHandler = fileURLToPath(new URL('./runtime/errorHandler', import.meta.url))
88+
})
89+
} else {
90+
const appInsightsServer = AppInsights.setup(options.serverConnectionString)
6091

61-
const optionsServer = deepMerge.all([options, privateRuntimeConfig])
92+
// Initialize AppInsights
93+
for (const [key, value] of Object.entries(options.serverConfig)) {
94+
AppInsights.defaultClient.config[key] = value
95+
}
6296

63-
if (!optionsServer.serverConnectionString) {
64-
logger.info('Server errors will not be logged because no serverConnectionString provided')
65-
return
66-
}
97+
if (options.initialize) {
98+
appInsightsServer.start()
99+
}
100+
const appInsightsClient = AppInsights.defaultClient
67101

68-
const appInsightsServer = AppInsights.setup(optionsServer.serverConnectionString)
102+
logger.success('Started logging errors to AppInsights')
69103

70-
// Initialize AppInsights
71-
for (const [key, value] of Object.entries(optionsServer.serverConfig)) {
72-
AppInsights.defaultClient.config[key] = value
73-
}
104+
addPluginTemplate({
105+
src: path.resolve(__dirname, 'appinsights.server.js'),
106+
fileName: 'appinsights.server.js',
107+
mode: 'server'
108+
})
74109

75-
if (optionsServer.initialize) {
76-
appInsightsServer.start()
110+
nuxt.hook('render:setupMiddleware', app => app.use(requestHandler(appInsightsClient)))
111+
nuxt.hook('render:errorMiddleware', app => app.use(errorHandler(appInsightsClient)))
112+
nuxt.hook('generate:routeFailed', ({ errors }) => {
113+
errors.forEach(({ error }) => {
114+
appInsightsClient.trackException({ exception: error })
115+
})
116+
})
117+
}
77118
}
78-
const appInsightsClient = AppInsights.defaultClient
79-
80-
logger.success('Started logging errors to AppInsights')
81-
82-
this.addPlugin({
83-
src: path.resolve(__dirname, 'appinsights.server.js'),
84-
fileName: 'appinsights.server.js',
85-
mode: 'server'
86-
})
87-
88-
this.nuxt.hook('render:setupMiddleware', app => app.use(requestHandler(appInsightsClient)))
89-
this.nuxt.hook('render:errorMiddleware', app => app.use(errorHandler(appInsightsClient)))
90-
this.nuxt.hook('generate:routeFailed', ({ errors }) => {
91-
errors.forEach(({ error }) => {
92-
appInsightsClient.trackException({ exception: error })
93-
})
94-
})
95119
}
96-
}
120+
})

lib/runtime/appinsights.init.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import * as AppInsights from 'applicationinsights'
2+
3+
let initialized = false
4+
5+
const getClient = (options, runtimeConfig) => {
6+
if (!initialized) {
7+
const runtimeConfigLocal = runtimeConfig.public.appInsights || {}
8+
const mergedConfig = { ...options, ...runtimeConfigLocal }
9+
const appInsightsServer = AppInsights.setup(mergedConfig.serverConnectionString)
10+
for (const [key, value] of Object.entries(mergedConfig.serverConfig)) {
11+
AppInsights.defaultClient.config[key] = value
12+
}
13+
if (mergedConfig.initialize) {
14+
appInsightsServer.start()
15+
}
16+
initialized = true
17+
}
18+
return AppInsights.default ? AppInsights.default.defaultClient : AppInsights.defaultClient
19+
}
20+
export default getClient

lib/runtime/appinsights.server.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { useRuntimeConfig } from '#imports'
2+
import getClient from './appinsights.init'
3+
4+
export default defineNuxtPlugin(nuxtApp => {
5+
const client = getClient(<%= serialize(options) %>, useRuntimeConfig())
6+
nuxtApp.provide('injected', () => 'my injected function')
7+
// now available on `nuxtApp.$injected`
8+
})

lib/runtime/errorHandler.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import getClient from './appinsights.init'
2+
import defaultErrorHandler from '#existing-error-handler'
3+
import { useRuntimeConfig } from '#imports'
4+
import options from '#appinsights-config'
5+
6+
export default function (error, event) {
7+
const client = getClient(options, useRuntimeConfig())
8+
client.trackException({
9+
exception: error,
10+
properties: {
11+
headers: event.req.headers
12+
}
13+
})
14+
defaultErrorHandler(error, event)
15+
}

lib/runtime/nitro.js

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import getClient from './appinsights.init'
2+
import { useRuntimeConfig } from '#imports'
3+
import options from '#appinsights-config'
4+
5+
export default function (nitro) {
6+
const client = getClient(options, useRuntimeConfig())
7+
nitro.hooks.hook('render:response', (response, { event }) => {
8+
if (typeof response.body === 'string' && (response.headers['Content-Type'] || response.headers['content-type'])?.includes('html')) {
9+
// We deliberately do not await so as not to block the response
10+
// appInsightsClient.trackRequest({ name: `${event.req.method} ${event.req.url}`, url: event.req.url, resultCode: response.statusCode, success: true, duration: 0 })
11+
client.trackNodeHttpRequest({ request: event.req, response })
12+
}
13+
})
14+
}

package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,17 @@
2222
"nuxt": "node --inspect=0.0.0.0 node_modules/nuxt-edge/bin/nuxt test/fixture"
2323
},
2424
"dependencies": {
25-
"applicationinsights": "^2.3.3",
26-
"@microsoft/applicationinsights-web": "^2.8.5",
27-
"consola": "^2.15.0",
25+
"@nuxt/kit": "^3.0.0-rc.10",
26+
"applicationinsights": "^2.3.5",
27+
"@microsoft/applicationinsights-web": "^2.8.7",
2828
"deepmerge": "^4.2.2"
2929
},
3030
"devDependencies": {
3131
"@nuxtjs/eslint-config": "^6.0.1",
3232
"codecov": "^3.8.2",
3333
"eslint": "^7.28.0",
3434
"jest": "^26.6.3",
35+
"nuxt": "npm:[email protected]",
3536
"nuxt-edge": "^2.16.0-27064814.35c6ac41",
3637
"standard-version": "^9.3.0"
3738
},

0 commit comments

Comments
 (0)