Skip to content

Commit

Permalink
feat(nextjs): Add instructions for custom _error page
Browse files Browse the repository at this point in the history
  • Loading branch information
lforst committed Nov 13, 2023
1 parent 959fbd5 commit a1f5368
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 0 deletions.
106 changes: 106 additions & 0 deletions src/nextjs/nextjs-wizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@ import {
} from '../utils/clack-utils';
import { SentryProjectData, WizardOptions } from '../utils/types';
import {
getFullUnderscoreErrorCopyPasteSnippet,
getNextjsConfigCjsAppendix,
getNextjsConfigCjsTemplate,
getNextjsConfigEsmCopyPasteSnippet,
getNextjsSentryBuildOptionsTemplate,
getNextjsWebpackPluginOptionsTemplate,
getSentryConfigContents,
getSentryDefaultUnderscoreErrorPage,
getSentryExampleApiRoute,
getSentryExampleAppDirApiRoute,
getSentryExamplePageContents,
getSimpleUnderscoreErrorCopyPasteSnippet,
} from './templates';
import { traceStep, withTelemetry } from '../telemetry';
import { getPackageVersion, hasPackageInstalled } from '../utils/package-json';
Expand Down Expand Up @@ -83,6 +86,109 @@ export async function runNextjsWizardWithTelemetry(
createOrMergeNextJsFiles(selectedProject, selfHosted, sentryUrl),
);

await traceStep('create-underscoreerror-page', async () => {
const srcDir = path.join(process.cwd(), 'src');
const maybePagesDirPath = path.join(process.cwd(), 'pages');
const maybeSrcPagesDirPath = path.join(srcDir, 'pages');

const pagesLocation =
fs.existsSync(maybePagesDirPath) &&
fs.lstatSync(maybePagesDirPath).isDirectory()
? ['pages']
: fs.existsSync(maybeSrcPagesDirPath) &&
fs.lstatSync(maybeSrcPagesDirPath).isDirectory()
? ['src', 'pages']
: undefined;

if (!pagesLocation) {
return;
}

const underscoreErrorPageFile = fs.existsSync(
path.join(process.cwd(), ...pagesLocation, '_error.tsx'),
)
? '_error.tsx'
: fs.existsSync(path.join(process.cwd(), ...pagesLocation, '_error.ts'))
? '_error.ts'
: fs.existsSync(path.join(process.cwd(), ...pagesLocation, '_error.jsx'))
? '_error.jsx'
: fs.existsSync(path.join(process.cwd(), ...pagesLocation, '_error.js'))
? '_error.js'
: undefined;

if (!underscoreErrorPageFile) {
await fs.promises.writeFile(
path.join(process.cwd(), ...pagesLocation, '_error.jsx'),
getSentryDefaultUnderscoreErrorPage(),
{ encoding: 'utf8', flag: 'w' },
);

clack.log.success(
`Created ${chalk.bold(path.join(...pagesLocation, '_error.jsx'))}.`,
);
} else if (
fs
.readFileSync(
path.join(process.cwd(), ...pagesLocation, underscoreErrorPageFile),
'utf8',
)
.includes('getInitialProps')
) {
clack.log.info(
`It seems like you already have a custom error page.\n\nPlease put the following function call in the ${chalk.bold(
'getInitialProps',
)}\nmethod of your custom error page at ${chalk.bold(
path.join(...pagesLocation, underscoreErrorPageFile),
)}:`,
);

// eslint-disable-next-line no-console
console.log(getSimpleUnderscoreErrorCopyPasteSnippet());

const shouldContinue = await abortIfCancelled(
clack.confirm({
message: `Did you modify your ${chalk.bold(
path.join(...pagesLocation, underscoreErrorPageFile),
)} file as described above?`,
active: 'Yes',
inactive: 'No, get me out of here',
}),
);

if (!shouldContinue) {
await abort();
}
} else {
clack.log.info(
`It seems like you already have a custom error page.\n\nPlease add the following code to your custom error page\nat ${chalk.bold(
path.join(...pagesLocation, underscoreErrorPageFile),
)}:`,
);

// eslint-disable-next-line no-console
console.log(
getFullUnderscoreErrorCopyPasteSnippet(
underscoreErrorPageFile === '_error.ts' ||
underscoreErrorPageFile === '_error.tsx',
),
);

const shouldContinue = await abortIfCancelled(
clack.confirm({
message: `Did add the code to your ${chalk.bold(
path.join(...pagesLocation, underscoreErrorPageFile),
)} file as described above?`,
active: 'Yes',
inactive: 'No, get me out of here',
}),
);

if (!shouldContinue) {
await abort();
}
}
});

await traceStep('create-example-page', async () =>
createExamplePage(selfHosted, selectedProject, sentryUrl),
);
Expand Down
57 changes: 57 additions & 0 deletions src/nextjs/templates.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import chalk from 'chalk';

export function getNextjsWebpackPluginOptionsTemplate(
orgSlug: string,
projectSlug: string,
Expand Down Expand Up @@ -267,3 +269,58 @@ export function GET() {
}
`;
}

export function getSentryDefaultUnderscoreErrorPage() {
return `import * as Sentry from "@sentry/nextjs";
import Error from "next/error";
const CustomErrorComponent = (props) => {
return <Error statusCode={props.statusCode} />;
};
CustomErrorComponent.getInitialProps = async (contextData) => {
// In case this is running in a serverless function, await this in order to give Sentry
// time to send the error before the lambda exits
await Sentry.captureUnderscoreErrorException(contextData);
// This will contain the status code of the response
return Error.getInitialProps(contextData);
};
export default CustomErrorComponent;
`;
}

export function getSimpleUnderscoreErrorCopyPasteSnippet() {
return `
${chalk.green(`import * as Sentry from '@sentry/nextjs';`)}
${chalk.dim(
'// Replace "YourCustomErrorComponent" with your custom error component!',
)}
YourCustomErrorComponent.getInitialProps = async (${chalk.green(
`contextData`,
)}) => {
${chalk.green('await Sentry.captureUnderscoreErrorException(contextData);')}
${chalk.dim('// ...other getInitialProps code')}
};
`;
}

export function getFullUnderscoreErrorCopyPasteSnippet(isTs: boolean) {
return `
import * as Sentry from '@sentry/nextjs';${
isTs ? '\nimport type { NextPageContext } from "next";' : ''
}
${chalk.dim(
'// Replace "YourCustomErrorComponent" with your custom error component!',
)}
YourCustomErrorComponent.getInitialProps = async (contextData${
isTs ? ': NextPageContext' : ''
}) => {
await Sentry.captureUnderscoreErrorException(contextData);
};
`;
}

0 comments on commit a1f5368

Please sign in to comment.