Skip to content

Commit

Permalink
ref(nextjs): Apply uncaught exception fix (#6138) only when middlewar…
Browse files Browse the repository at this point in the history
…e is configured
  • Loading branch information
lforst committed Nov 4, 2022
1 parent 8073954 commit 38aba99
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 10 deletions.
6 changes: 4 additions & 2 deletions packages/nextjs/src/config/loaders/prefixLoader.ts
Expand Up @@ -5,22 +5,24 @@ import { LoaderThis } from './types';

type LoaderOptions = {
distDir: string;
hasMiddleware: boolean;
};

/**
* Inject templated code into the beginning of a module.
*/
export default function prefixLoader(this: LoaderThis<LoaderOptions>, userCode: string): string {
// We know one or the other will be defined, depending on the version of webpack being used
const { distDir } = 'getOptions' in this ? this.getOptions() : this.query;
const { distDir, hasMiddleware } = 'getOptions' in this ? this.getOptions() : this.query;

const templatePath = path.resolve(__dirname, '../templates/prefixLoaderTemplate.js');
// make sure the template is included when runing `webpack watch`
this.addDependency(templatePath);

// Fill in the placeholder
// Fill in placehoolders
let templateCode = fs.readFileSync(templatePath).toString();
templateCode = templateCode.replace('__DIST_DIR__', distDir.replace(/\\/g, '\\\\'));
templateCode = templateCode.replace('__HAS_MIDDLEWARE__', hasMiddleware ? 'true' : 'false');

return `${templateCode}\n${userCode}`;
}
4 changes: 4 additions & 0 deletions packages/nextjs/src/config/templates/prefixLoaderTemplate.ts
@@ -1,5 +1,9 @@
declare const __HAS_MIDDLEWARE__: boolean;

// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
(global as any).__rewriteFramesDistDir__ = '__DIST_DIR__';
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
(global as any).__hasMiddleware__ = __HAS_MIDDLEWARE__;

// We need this to make this file an ESM module, which TS requires when using `isolatedModules`, but it doesn't affect
// the end result - Rollup recognizes that it's a no-op and doesn't include it when building our code.
Expand Down
10 changes: 8 additions & 2 deletions packages/nextjs/src/config/webpack.ts
Expand Up @@ -58,6 +58,13 @@ export function constructWebpackConfigFunction(
}

if (isServer) {
const pagesDir = newConfig.resolve?.alias?.['private-next-pages'] as string;

const middlewareJsPath = path.join(pagesDir, '..', 'middleware.js');
const middlewareTsPath = path.join(pagesDir, '..', 'middleware.ts');

const hasMiddleware = fs.existsSync(middlewareJsPath) || fs.existsSync(middlewareTsPath);

newConfig.module = {
...newConfig.module,
rules: [
Expand All @@ -72,6 +79,7 @@ export function constructWebpackConfigFunction(
loader: path.resolve(__dirname, 'loaders/prefixLoader.js'),
options: {
distDir: userNextConfig.distDir || '.next',
hasMiddleware,
},
},
],
Expand All @@ -80,8 +88,6 @@ export function constructWebpackConfigFunction(
};

if (userSentryOptions.autoInstrumentServerFunctions !== false) {
const pagesDir = newConfig.resolve?.alias?.['private-next-pages'] as string;

// Default page extensions per https://github.com/vercel/next.js/blob/f1dbc9260d48c7995f6c52f8fbcc65f08e627992/packages/next/server/config-shared.ts#L161
const pageExtensions = userNextConfig.pageExtensions || ['tsx', 'ts', 'jsx', 'js'];
const pageExtensionRegex = pageExtensions.map(escapeStringForRegex).join('|');
Expand Down
17 changes: 11 additions & 6 deletions packages/nextjs/src/index.server.ts
Expand Up @@ -19,7 +19,10 @@ export { captureUnderscoreErrorException } from './utils/_error';
// because or SSR of next.js we can only use this.
export { ErrorBoundary, showReportDialog, withErrorBoundary } from '@sentry/react';

type GlobalWithDistDir = typeof global & { __rewriteFramesDistDir__: string };
type GlobalWithBuildTimeConfiguration = typeof global & {
__rewriteFramesDistDir__: string;
__hasMiddleware__: boolean;
};
const domain = domainModule as typeof domainModule & { active: (domainModule.Domain & Carrier) | null };

// Exporting this constant means we can compute it without the linter complaining, even if we stop directly using it in
Expand Down Expand Up @@ -104,7 +107,7 @@ function addServerIntegrations(options: NextjsOptions): void {

// This value is injected at build time, based on the output directory specified in the build config. Though a default
// is set there, we set it here as well, just in case something has gone wrong with the injection.
const distDirName = (global as GlobalWithDistDir).__rewriteFramesDistDir__ || '.next';
const distDirName = (global as GlobalWithBuildTimeConfiguration).__rewriteFramesDistDir__ || '.next';
// nextjs always puts the build directory at the project root level, which is also where you run `next start` from, so
// we can read in the project directory from the currently running process
const distDirAbsPath = path.resolve(process.cwd(), distDirName);
Expand All @@ -118,10 +121,12 @@ function addServerIntegrations(options: NextjsOptions): void {
});
integrations = addOrUpdateIntegration(defaultRewriteFramesIntegration, integrations);

const nativeBehaviourOnUncaughtException = new Integrations.OnUncaughtException();
integrations = addOrUpdateIntegration(nativeBehaviourOnUncaughtException, integrations, {
_options: { exitEvenIfOtherHandlersAreRegistered: false },
});
if ((global as GlobalWithBuildTimeConfiguration).__hasMiddleware__) {
const nativeBehaviourOnUncaughtException = new Integrations.OnUncaughtException();
integrations = addOrUpdateIntegration(nativeBehaviourOnUncaughtException, integrations, {
_options: { exitEvenIfOtherHandlersAreRegistered: false },
});
}

if (hasTracingEnabled(options)) {
const defaultHttpTracingIntegration = new Integrations.Http({ tracing: true });
Expand Down

0 comments on commit 38aba99

Please sign in to comment.