UNPKG

@honeybadger-io/nextjs

Version:
263 lines (255 loc) 11.3 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var fs = require('fs'); var path = require('path'); var HoneybadgerSourceMapPlugin = require('@honeybadger-io/webpack'); var Honeybadger = require('@honeybadger-io/js'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs); var path__default = /*#__PURE__*/_interopDefaultLegacy(path); var HoneybadgerSourceMapPlugin__default = /*#__PURE__*/_interopDefaultLegacy(HoneybadgerSourceMapPlugin); var Honeybadger__default = /*#__PURE__*/_interopDefaultLegacy(Honeybadger); const URL_DOCS_SOURCE_MAPS_UPLOAD = 'https://docs.honeybadger.io/lib/javascript/integration/nextjs/#source-map-upload-and-tracking-deploys'; let _silent = true; function log(type, msg) { if (['error', 'warn'].includes(type) || !_silent) { console[type]('[HoneybadgerNextJs]', msg); } } function shouldUploadSourceMaps(honeybadgerNextJsConfig, context) { const { dev } = context; if (honeybadgerNextJsConfig.disableSourceMapUpload) { return false; } if (!honeybadgerNextJsConfig.webpackPluginOptions || !honeybadgerNextJsConfig.webpackPluginOptions.apiKey) { log('warn', `skipping source map upload; here's how to enable: ${URL_DOCS_SOURCE_MAPS_UPLOAD}`); return false; } if (dev || process.env.NODE_ENV === 'development') { return false; } return true; } function mergeWithExistingWebpackConfig(nextJsWebpackConfig, honeybadgerNextJsConfig) { return function webpackFunctionMergedWithHb(webpackConfig, context) { const { isServer, dir: projectDir, nextRuntime } = context; const configType = isServer ? (nextRuntime === 'edge' ? 'edge' : 'server') : 'browser'; log('debug', `reached webpackFunctionMergedWithHb isServer[${isServer}] configType[${configType}]`); let result = { ...webpackConfig }; if (typeof nextJsWebpackConfig === 'function') { result = nextJsWebpackConfig(result, context); } const originalEntry = result.entry; result.entry = async () => injectHoneybadgerConfigToEntry(originalEntry, projectDir, configType); if (shouldUploadSourceMaps(honeybadgerNextJsConfig, context)) { // `result.devtool` must be 'hidden-source-map' or 'source-map' to properly pass sourcemaps. // Next.js uses regular `source-map` which doesnt pass its sourcemaps to Webpack. // https://github.com/vercel/next.js/blob/89ec21ed686dd79a5770b5c669abaff8f55d8fef/packages/next/build/webpack/config/blocks/base.ts#L40 // Use the hidden-source-map option when you don't want the source maps to be // publicly available on the servers, only to the error reporting result.devtool = 'hidden-source-map'; if (!result.plugins) { result.plugins = []; } const options = getWebpackPluginOptions(honeybadgerNextJsConfig); if (options) { result.plugins.push(new HoneybadgerSourceMapPlugin__default["default"](options)); } } return result; }; } async function injectHoneybadgerConfigToEntry(originalEntry, projectDir, configType) { const result = typeof originalEntry === 'function' ? await originalEntry() : { ...originalEntry }; const hbConfigFile = getHoneybadgerConfigFile(projectDir, configType); if (!hbConfigFile) { return result; } const hbConfigFileRelativePath = `./${hbConfigFile}`; if (!Object.keys(result).length) { log('debug', `no entry points for configType[${configType}]`); } for (const entryName in result) { addHoneybadgerConfigToEntry(result, entryName, hbConfigFileRelativePath, configType); } return result; } function addHoneybadgerConfigToEntry(entry, entryName, hbConfigFile, configType) { log('debug', `adding entry[${entryName}] to configType[${configType}]`); switch (configType) { case 'server': if (!entryName.startsWith('pages/')) { return; } break; case 'browser': if (!['pages/_app', 'main-app'].includes(entryName)) { return; } break; } const currentEntryPoint = entry[entryName]; let newEntryPoint = currentEntryPoint; if (typeof currentEntryPoint === 'string') { newEntryPoint = [hbConfigFile, currentEntryPoint]; } else if (Array.isArray(currentEntryPoint)) { newEntryPoint = [hbConfigFile, ...currentEntryPoint]; } // descriptor object (webpack 5+) else if (typeof currentEntryPoint === 'object' && currentEntryPoint && 'import' in currentEntryPoint) { const currentImportValue = currentEntryPoint['import']; const newImportValue = [hbConfigFile]; if (typeof currentImportValue === 'string') { newImportValue.push(currentImportValue); } else { newImportValue.push(...(currentImportValue)); } newEntryPoint = { ...currentEntryPoint, import: newImportValue, }; } else { log('error', 'Could not inject Honeybadger config to entry point: ' + JSON.stringify(currentEntryPoint, null, 2)); } entry[entryName] = newEntryPoint; } function getHoneybadgerConfigFile(projectDir, configType) { const possibilities = [`honeybadger.${configType}.config.ts`, `honeybadger.${configType}.config.js`]; for (const filename of possibilities) { if (fs__default["default"].existsSync(path__default["default"].resolve(projectDir, filename))) { return filename; } } log('debug', `could not find config file in ${projectDir} for ${configType}`); return null; } function getWebpackPluginOptions(honeybadgerNextJsConfig) { var _a, _b, _c; const apiKey = ((_a = honeybadgerNextJsConfig.webpackPluginOptions) === null || _a === void 0 ? void 0 : _a.apiKey) || process.env.NEXT_PUBLIC_HONEYBADGER_API_KEY; const assetsUrl = ((_b = honeybadgerNextJsConfig.webpackPluginOptions) === null || _b === void 0 ? void 0 : _b.assetsUrl) || process.env.NEXT_PUBLIC_HONEYBADGER_ASSETS_URL; if (!apiKey || !assetsUrl) { log('error', 'Missing Honeybadger required configuration for webpack plugin. Source maps will not be uploaded to Honeybadger.'); return null; } return { ...honeybadgerNextJsConfig.webpackPluginOptions, apiKey, assetsUrl, revision: ((_c = honeybadgerNextJsConfig.webpackPluginOptions) === null || _c === void 0 ? void 0 : _c.revision) || process.env.NEXT_PUBLIC_HONEYBADGER_REVISION, silent: _silent, }; } function getNextJsVersionInstalled() { var _a; try { return (_a = require('next/package.json').version) === null || _a === void 0 ? void 0 : _a.split('.'); } catch (e) { return null; } } /** * NextJs will report a warning if the `serverExternalPackages` option is not present. * This is because @honeybadger-io/js will try to require configuration files dynamically (https://github.com/honeybadger-io/honeybadger-js/pull/1268). * * First reported here: https://github.com/honeybadger-io/honeybadger-js/issues/1351 */ function addServerExternalPackagesOption(config) { var _a, _b; // this should be available in the upcoming version of Next.js (14.3.0) if (config.serverExternalPackages && Array.isArray(config.serverExternalPackages)) { log('debug', 'adding @honeybadger-io/js to serverExternalPackages'); config.serverExternalPackages.push('@honeybadger-io/js'); return; } if (((_a = config.experimental) === null || _a === void 0 ? void 0 : _a.serverComponentsExternalPackages) && Array.isArray((_b = config.experimental) === null || _b === void 0 ? void 0 : _b.serverComponentsExternalPackages)) { log('debug', 'adding @honeybadger-io/js to experimental.serverComponentsExternalPackages'); config.experimental.serverComponentsExternalPackages.push('@honeybadger-io/js'); return; } const nextJsVersion = getNextJsVersionInstalled(); if (nextJsVersion) { if ((+nextJsVersion[0] === 14 && +nextJsVersion[1] >= 3) || +nextJsVersion[0] > 14) { log('debug', 'adding serverExternalPackages option with value ["@honeybadger-io/js"]'); config.serverExternalPackages = ['@honeybadger-io/js']; } else { log('debug', 'adding experimental.serverComponentsExternalPackages option with value ["@honeybadger-io/js"]'); if (!config.experimental) { config.experimental = {}; } config.experimental.serverComponentsExternalPackages = ['@honeybadger-io/js']; } } } function setupHoneybadger(config, honeybadgerNextJsConfig) { var _a; if (!honeybadgerNextJsConfig) { honeybadgerNextJsConfig = { silent: true, disableSourceMapUpload: false, }; } _silent = (_a = honeybadgerNextJsConfig.silent) !== null && _a !== void 0 ? _a : true; addServerExternalPackagesOption(config); return { ...config, webpack: mergeWithExistingWebpackConfig(config.webpack, honeybadgerNextJsConfig) }; } function configure() { var _a; if (((_a = Honeybadger__default["default"].config.apiKey) === null || _a === void 0 ? void 0 : _a.length) > 0) { return; } let projectRoot = undefined; try { // not available on edge runtime projectRoot = process.cwd(); } // eslint-disable-next-line @typescript-eslint/no-unused-vars catch (error) { // do nothing } Honeybadger__default["default"] .configure({ apiKey: process.env.NEXT_PUBLIC_HONEYBADGER_API_KEY, environment: process.env.NEXT_PUBLIC_VERCEL_ENV || process.env.VERCEL_ENV || process.env.NODE_ENV, revision: process.env.NEXT_PUBLIC_HONEYBADGER_REVISION, projectRoot: 'webpack://_N_E/./' }) .beforeNotify((notice) => { if (!projectRoot) { return; } notice === null || notice === void 0 ? void 0 : notice.backtrace.forEach((line) => { if (line.file) { line.file = line.file.replace(`${projectRoot}/.next/server`, `${process.env.NEXT_PUBLIC_HONEYBADGER_ASSETS_URL}/..`); } return line; }); }); } /** * Wraps a handler function with Honeybadger error reporting. * Use with Next.js API route handlers or middleware. */ function withHoneybadger(handler) { configure(); return new Proxy(handler, { apply: async (target, thisArg, args) => { try { return await Reflect.apply(target, thisArg, args); } catch (error) { await Honeybadger__default["default"].notifyAsync(error); throw error; // Re-throw the error after reporting it } }, }); } exports.setupHoneybadger = setupHoneybadger; exports.withHoneybadger = withHoneybadger; //# sourceMappingURL=honeybadger-nextjs.cjs.js.map