Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 116 additions & 53 deletions src/nextjs/nextjs-wizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
getSimpleUnderscoreErrorCopyPasteSnippet,
getWithSentryConfigOptionsTemplate,
getInstrumentationClientHookCopyPasteSnippet,
getAiRulesFileContent,
} from './templates';
import { getNextJsVersionBucket } from './utils';

Expand Down Expand Up @@ -122,12 +123,12 @@

const pagesLocation =
fs.existsSync(maybePagesDirPath) &&
fs.lstatSync(maybePagesDirPath).isDirectory()
fs.lstatSync(maybePagesDirPath).isDirectory()

Check warning on line 126 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L126

Added line #L126 was not covered by tests
? ['pages']
: fs.existsSync(maybeSrcPagesDirPath) &&
fs.lstatSync(maybeSrcPagesDirPath).isDirectory()
? ['src', 'pages']
: undefined;
? ['src', 'pages']
: undefined;

Check warning on line 131 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L130-L131

Added lines #L130 - L131 were not covered by tests

if (!pagesLocation) {
return;
Expand All @@ -138,12 +139,12 @@
)
? '_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;
? '_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;

Check warning on line 147 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L142-L147

Added lines #L142 - L147 were not covered by tests

if (!underscoreErrorPageFile) {
await fs.promises.writeFile(
Expand Down Expand Up @@ -198,7 +199,7 @@
console.log(
getFullUnderscoreErrorCopyPasteSnippet(
underscoreErrorPageFile === '_error.ts' ||
underscoreErrorPageFile === '_error.tsx',
underscoreErrorPageFile === '_error.tsx',

Check warning on line 202 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L202

Added line #L202 was not covered by tests
),
);

Expand All @@ -224,12 +225,12 @@

const appDirLocation =
fs.existsSync(maybeAppDirPath) &&
fs.lstatSync(maybeAppDirPath).isDirectory()
fs.lstatSync(maybeAppDirPath).isDirectory()

Check warning on line 228 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L228

Added line #L228 was not covered by tests
? ['app']
: fs.existsSync(maybeSrcAppDirPath) &&
fs.lstatSync(maybeSrcAppDirPath).isDirectory()
? ['src', 'app']
: undefined;
? ['src', 'app']
: undefined;

Check warning on line 233 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L232-L233

Added lines #L232 - L233 were not covered by tests

if (!appDirLocation) {
return;
Expand All @@ -240,23 +241,22 @@
)
? 'global-error.tsx'
: fs.existsSync(
path.join(process.cwd(), ...appDirLocation, 'global-error.ts'),
)
? 'global-error.ts'
: fs.existsSync(
path.join(process.cwd(), ...appDirLocation, 'global-error.ts'),
)
? 'global-error.ts'
: fs.existsSync(

Check warning on line 247 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L244-L247

Added lines #L244 - L247 were not covered by tests
path.join(process.cwd(), ...appDirLocation, 'global-error.jsx'),
)
? 'global-error.jsx'
: fs.existsSync(
path.join(process.cwd(), ...appDirLocation, 'global-error.js'),
)
? 'global-error.js'
: undefined;
? 'global-error.jsx'
: fs.existsSync(
path.join(process.cwd(), ...appDirLocation, 'global-error.js'),
)
? 'global-error.js'
: undefined;

Check warning on line 255 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L250-L255

Added lines #L250 - L255 were not covered by tests

if (!globalErrorPageFile) {
const newGlobalErrorFileName = `global-error.${
typeScriptDetected ? 'tsx' : 'jsx'
}`;
const newGlobalErrorFileName = `global-error.${typeScriptDetected ? 'tsx' : 'jsx'
}`;

Check warning on line 259 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L258-L259

Added lines #L258 - L259 were not covered by tests

await fs.promises.writeFile(
path.join(process.cwd(), ...appDirLocation, newGlobalErrorFileName),
Expand All @@ -280,7 +280,7 @@
console.log(
getGlobalErrorCopyPasteSnippet(
globalErrorPageFile === 'global-error.ts' ||
globalErrorPageFile === 'global-error.tsx',
globalErrorPageFile === 'global-error.tsx',

Check warning on line 283 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L283

Added line #L283 was not covered by tests
),
);

Expand Down Expand Up @@ -344,24 +344,57 @@
const packageManagerForOutro =
packageManagerFromInstallStep ?? (await getPackageManager());

await traceStep('create-ai-rules-file', async () => {
const shouldCreateAiRulesFile = await askShouldCreateAiRulesFile();
if (shouldCreateAiRulesFile) {
try {
const rulesDir = path.join(process.cwd(), '.rules');
const rulesDirExists = fs.existsSync(rulesDir);

Check warning on line 352 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L347-L352

Added lines #L347 - L352 were not covered by tests

// Create .rules directory if it doesn't exist
if (!rulesDirExists) {
await fs.promises.mkdir(rulesDir, { recursive: true });
}

Check warning on line 357 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L355-L357

Added lines #L355 - L357 were not covered by tests

const aiRulesContent = getAiRulesFileContent();
await fs.promises.writeFile(
path.join(rulesDir, 'sentryrules.md'),
aiRulesContent,
{ encoding: 'utf8', flag: 'w' },
);

Check warning on line 364 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L359-L364

Added lines #L359 - L364 were not covered by tests

clack.log.success(
`Created ${chalk.cyan('sentryrules.md')} in ${chalk.cyan('.rules')} directory.`,
);
} catch (error) {
clack.log.error(
`Failed to create ${chalk.cyan(
'sentryrules.md',
)} in ${chalk.cyan('.rules')} directory. Please create it manually.`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we give them a link for the content to put this into the file? Or even a curl one-liner?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I love that idea. Sort of a "Couldn't do it, you can create it manually though!". Great suggestion.

);
Sentry.captureException(error);
}
} else {
clack.log.info('Skipped creating sentryrules.md.');
}
});

Check warning on line 380 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L366-L380

Added lines #L366 - L380 were not covered by tests

await runPrettierIfInstalled({ cwd: undefined });

clack.outro(`
${chalk.green('Successfully installed the Sentry Next.js SDK!')} ${
shouldCreateExamplePage
${chalk.green('Successfully installed the Sentry Next.js SDK!')} ${shouldCreateExamplePage

Check warning on line 385 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L385

Added line #L385 was not covered by tests
? `\n\nYou can validate your setup by (re)starting your dev environment (e.g. ${chalk.cyan(
`${packageManagerForOutro.runScriptCommand} dev`,
)}) and visiting ${chalk.cyan('"/sentry-example-page"')}`
`${packageManagerForOutro.runScriptCommand} dev`,
)}) and visiting ${chalk.cyan('"/sentry-example-page"')}`

Check warning on line 388 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L387-L388

Added lines #L387 - L388 were not covered by tests
: ''
}${
shouldCreateExamplePage && isLikelyUsingTurbopack
}${shouldCreateExamplePage && isLikelyUsingTurbopack

Check warning on line 390 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L390

Added line #L390 was not covered by tests
? `\nDon't forget to remove \`--turbo\` or \`--turbopack\` from your dev command until you have verified the SDK is working. You can safely add it back afterwards.`
: ''
}
}

Check warning on line 393 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L393

Added line #L393 was not covered by tests
${chalk.dim(
'If you encounter any issues, let us know here: https://github.com/getsentry/sentry-javascript/issues',
)}`);
'If you encounter any issues, let us know here: https://github.com/getsentry/sentry-javascript/issues',
)}`);

Check warning on line 397 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L396-L397

Added lines #L396 - L397 were not covered by tests
}

type SDKConfigOptions = {
Expand Down Expand Up @@ -503,9 +536,8 @@
}
}

const newInstrumentationFileName = `instrumentation.${
typeScriptDetected ? 'ts' : 'js'
}`;
const newInstrumentationFileName = `instrumentation.${typeScriptDetected ? 'ts' : 'js'
}`;

Check warning on line 540 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L539-L540

Added lines #L539 - L540 were not covered by tests

if (instrumentationHookLocation === 'does-not-exist') {
let newInstrumentationHookLocation: 'root' | 'src';
Expand Down Expand Up @@ -542,8 +574,8 @@
srcInstrumentationTsExists || instrumentationTsExists
? 'instrumentation.ts'
: srcInstrumentationJsExists || instrumentationJsExists
? 'instrumentation.js'
: newInstrumentationFileName,
? 'instrumentation.js'
: newInstrumentationFileName,

Check warning on line 578 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L577-L578

Added lines #L577 - L578 were not covered by tests
codeSnippet: getInstrumentationHookCopyPasteSnippet(
instrumentationHookLocation,
),
Expand Down Expand Up @@ -595,9 +627,8 @@
}
}

const newInstrumentationClientFileName = `instrumentation-client.${
typeScriptDetected ? 'ts' : 'js'
}`;
const newInstrumentationClientFileName = `instrumentation-client.${typeScriptDetected ? 'ts' : 'js'
}`;

Check warning on line 631 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L630-L631

Added lines #L630 - L631 were not covered by tests

if (instrumentationClientHookLocation === 'does-not-exist') {
let newInstrumentationClientHookLocation: 'root' | 'src';
Expand Down Expand Up @@ -638,8 +669,8 @@
srcInstrumentationClientTsExists || instrumentationClientTsExists
? 'instrumentation-client.ts'
: srcInstrumentationClientJsExists || instrumentationClientJsExists
? 'instrumentation-client.js'
: newInstrumentationClientFileName,
? 'instrumentation-client.js'
: newInstrumentationClientFileName,

Check warning on line 673 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L672-L673

Added lines #L672 - L673 were not covered by tests
codeSnippet: getInstrumentationClientHookCopyPasteSnippet(
selectedProject.keys[0].dsn.public,
selectedFeatures,
Expand Down Expand Up @@ -887,14 +918,14 @@
const appFolderLocation = hasRootAppDirectory
? ['app']
: hasSrcAppDirectory
? ['src', 'app']
: undefined;
? ['src', 'app']
: undefined;

Check warning on line 922 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L921-L922

Added lines #L921 - L922 were not covered by tests

let pagesFolderLocation = hasRootPagesDirectory
? ['pages']
: hasSrcPagesDirectory
? ['src', 'pages']
: undefined;
? ['src', 'pages']
: undefined;

Check warning on line 928 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L927-L928

Added lines #L927 - L928 were not covered by tests

// If the user has neither pages nor app directory we create a pages folder for them
if (!appFolderLocation && !pagesFolderLocation) {
Expand All @@ -918,9 +949,8 @@
if (!hasRootLayout) {
// In case no root layout file exists, we create a simple one so that
// the example page can be rendered correctly.
const newRootLayoutFilename = `layout.${
typeScriptDetected ? 'tsx' : 'jsx'
}`;
const newRootLayoutFilename = `layout.${typeScriptDetected ? 'tsx' : 'jsx'
}`;

Check warning on line 953 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L952-L953

Added lines #L952 - L953 were not covered by tests

await fs.promises.writeFile(
path.join(appFolderPath, newRootLayoutFilename),
Expand Down Expand Up @@ -1071,6 +1101,39 @@
});
}

/**
* Ask users if they want to create a .sentryrules file with AI rule examples for Sentry.
* This is useful for giving the LLM context on common actions in Sentry like custom spans,
* logging, and error / exception handling.
*/

async function askShouldCreateAiRulesFile(): Promise<boolean> {
return await traceStep('ask-create-ai-rules-file', async (span) => {
const shouldCreateAiRulesFile = await abortIfCancelled(
clack.select({
message: 'Do you want to create a .sentryrules file with AI rule examples for Sentry?',
options: [
{
label: 'Yes',
value: true,
hint: 'Creates .sentryrules in your project root',
},
{
label: 'No',
value: false,
},
],
initialValue: true,
}),
);

Check warning on line 1128 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L1110-L1128

Added lines #L1110 - L1128 were not covered by tests

span?.setAttribute('shouldCreateAiRulesFile', shouldCreateAiRulesFile);
Sentry.setTag('shouldCreateAiRulesFile', shouldCreateAiRulesFile);

Check warning on line 1131 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L1130-L1131

Added lines #L1130 - L1131 were not covered by tests

return shouldCreateAiRulesFile;
});
}

Check warning on line 1135 in src/nextjs/nextjs-wizard.ts

View check run for this annotation

Codecov / codecov/patch

src/nextjs/nextjs-wizard.ts#L1133-L1135

Added lines #L1133 - L1135 were not covered by tests

/**
* Returns true or false depending on whether we think the user is using Turbopack. May return null in case we aren't sure.
*/
Expand Down
Loading
Loading