Skip to content

Commit 2fa5c7d

Browse files
committed
fix(nextjs): copy _error.js component
1 parent 1a6914b commit 2fa5c7d

File tree

4 files changed

+66
-14
lines changed

4 files changed

+66
-14
lines changed

packages/nextjs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"name": "Pangratios Cosma",
1313
"email": "pangratios.cosma@honeybadger.io"
1414
},
15-
"homepage": "https://github.com/honeybadger-io/honeybadger-js/packages/nextjs#readme",
15+
"homepage": "https://github.com/honeybadger-io/honeybadger-js/tree/master/packages/nextjs",
1616
"license": "MIT",
1717
"main": "dist/honeybadger-nextjs.cjs.js",
1818
"module": "dist/honeybadger-nextjs.esm.js",

packages/nextjs/src/copy-config-files.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,36 @@ const fs = require('fs')
55

66
const debug = process.env.HONEYBADGER_DEBUG === 'true'
77

8+
async function copyErrorJs() {
9+
const targetPath = path.join('pages', '_error.js')
10+
const errorJsAlreadyExists = fs.existsSync(targetPath)
11+
if (errorJsAlreadyExists) {
12+
// Don't overwrite an existing _error.js file.
13+
// Create a _error.js.bak file instead.
14+
const backupPath = path.join('pages', '_error.js.bak')
15+
await fs.promises.copyFile(targetPath, backupPath)
16+
}
17+
18+
const sourcePath = path.resolve(__dirname, '../templates/_error.js')
19+
20+
return fs.promises.copyFile(sourcePath, targetPath)
21+
}
22+
823
async function copyConfigFiles() {
924
if (debug) {
1025
console.debug('cwd', process.cwd())
1126
}
1227

13-
const templateDir = path.resolve(__dirname, '../templates');
14-
const files = await fs.promises.readdir(templateDir);
28+
const templateDir = path.resolve(__dirname, '../templates')
29+
const files = await fs.promises.readdir(templateDir)
1530
const copyPromises = files.map((file) => {
1631
if (debug) {
1732
console.debug('copying', file)
1833
}
19-
return fs.promises.copyFile(path.join(templateDir, file), file);
34+
35+
return file === '_error.js' ? copyErrorJs() : fs.promises.copyFile(path.join(templateDir, file), file)
2036
});
2137
await Promise.all(copyPromises);
22-
2338
console.log('Done copying config files.')
2439
}
2540

packages/nextjs/src/index.ts

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import fs from 'fs'
22
import path from 'path'
3+
import Honeybadger from '@honeybadger-io/js';
34
import HoneybadgerSourceMapPlugin from '@honeybadger-io/webpack'
45
import type { WebpackConfigContext } from 'next/dist/server/config-shared';
56
import { HoneybadgerNextJsConfig, NextJsRuntime, HoneybadgerWebpackPluginOptions } from './types'
@@ -25,15 +26,6 @@ function shouldUploadSourceMaps(honeybadgerNextJsConfig: HoneybadgerNextJsConfig
2526
return true
2627
}
2728

28-
export function setupHoneybadger(config, honeybadgerNextJsConfig: HoneybadgerNextJsConfig) {
29-
_silent = honeybadgerNextJsConfig?.silent ?? true
30-
31-
return {
32-
...config,
33-
webpack: mergeWithExistingWebpackConfig(config.webpack, honeybadgerNextJsConfig)
34-
}
35-
}
36-
3729
function mergeWithExistingWebpackConfig(nextJsWebpackConfig, honeybadgerNextJsConfig: HoneybadgerNextJsConfig) {
3830
return function webpackFunctionMergedWithHb(webpackConfig, context: WebpackConfigContext) {
3931

@@ -167,3 +159,33 @@ function getWebpackPluginOptions(honeybadgerNextJsConfig: HoneybadgerNextJsConfi
167159
silent: _silent,
168160
}
169161
}
162+
163+
export function setupHoneybadger(config, honeybadgerNextJsConfig: HoneybadgerNextJsConfig) {
164+
_silent = honeybadgerNextJsConfig?.silent ?? true
165+
166+
return {
167+
...config,
168+
webpack: mergeWithExistingWebpackConfig(config.webpack, honeybadgerNextJsConfig)
169+
}
170+
}
171+
172+
export async function notifyFromNextErrorComponent(contextData) {
173+
const { req, res, err } = contextData;
174+
175+
// exclude 40x except when this component is rendered from a routing error or a custom server
176+
// https://nextjs.org/docs/advanced-features/custom-error-page#caveats
177+
const statusCode = (res && res.statusCode) || contextData.statusCode;
178+
if (statusCode && statusCode < 500) {
179+
Honeybadger.config.logger.debug(`_error.js skipping because statusCode is ${statusCode}`);
180+
return Promise.resolve();
181+
}
182+
183+
await Honeybadger.notifyAsync(err || `_error.js called with falsy error (${err})`, {
184+
context:
185+
{
186+
url: req.url,
187+
method: req.method,
188+
statusCode: res.statusCode,
189+
}
190+
});
191+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// eslint-disable-next-line import/no-unresolved
2+
import { notifyFromNextErrorComponent } from '@honeybadger-io/nextjs';
3+
import NextErrorComponent from 'next/error';
4+
5+
const CustomErrorComponent = props => {
6+
return <NextErrorComponent statusCode={props.statusCode} />;
7+
};
8+
9+
CustomErrorComponent.getInitialProps = async contextData => {
10+
await notifyFromNextErrorComponent(contextData);
11+
12+
return NextErrorComponent.getInitialProps(contextData);
13+
};
14+
15+
export default CustomErrorComponent;

0 commit comments

Comments
 (0)