@sentry/wizard 3.35.0 → 3.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/e2e-tests/tests/nuxt-3.test.js +45 -25
  3. package/dist/e2e-tests/tests/nuxt-3.test.js.map +1 -1
  4. package/dist/e2e-tests/tests/nuxt-4.test.js +45 -25
  5. package/dist/e2e-tests/tests/nuxt-4.test.js.map +1 -1
  6. package/dist/e2e-tests/utils/index.js.map +1 -1
  7. package/dist/package.json +1 -1
  8. package/dist/src/android/templates.js +1 -1
  9. package/dist/src/android/templates.js.map +1 -1
  10. package/dist/src/nuxt/nuxt-wizard.js +36 -23
  11. package/dist/src/nuxt/nuxt-wizard.js.map +1 -1
  12. package/dist/src/nuxt/sdk-setup.d.ts +8 -1
  13. package/dist/src/nuxt/sdk-setup.js +149 -35
  14. package/dist/src/nuxt/sdk-setup.js.map +1 -1
  15. package/dist/src/nuxt/templates.d.ts +1 -1
  16. package/dist/src/nuxt/templates.js +5 -3
  17. package/dist/src/nuxt/templates.js.map +1 -1
  18. package/dist/src/nuxt/types.d.ts +2 -0
  19. package/dist/src/nuxt/types.js +10 -0
  20. package/dist/src/nuxt/types.js.map +1 -0
  21. package/dist/src/nuxt/utils.js +42 -6
  22. package/dist/src/nuxt/utils.js.map +1 -1
  23. package/dist/src/utils/clack-utils.d.ts +9 -1
  24. package/dist/src/utils/clack-utils.js +81 -23
  25. package/dist/src/utils/clack-utils.js.map +1 -1
  26. package/dist/src/utils/package-json.d.ts +5 -0
  27. package/dist/src/utils/package-json.js.map +1 -1
  28. package/dist/src/utils/package-manager.d.ts +1 -0
  29. package/dist/src/utils/package-manager.js +129 -0
  30. package/dist/src/utils/package-manager.js.map +1 -1
  31. package/dist/test/nuxt/templates.test.js +10 -1
  32. package/dist/test/nuxt/templates.test.js.map +1 -1
  33. package/e2e-tests/test-applications/nuxt-4-test-app/README.md +75 -0
  34. package/e2e-tests/test-applications/nuxt-4-test-app/nuxt.config.ts +6 -0
  35. package/e2e-tests/test-applications/nuxt-4-test-app/package.json +18 -0
  36. package/e2e-tests/test-applications/nuxt-4-test-app/public/favicon.ico +0 -0
  37. package/e2e-tests/test-applications/nuxt-4-test-app/public/robots.txt +1 -0
  38. package/e2e-tests/tests/nuxt-3.test.ts +23 -3
  39. package/e2e-tests/tests/nuxt-4.test.ts +22 -2
  40. package/e2e-tests/utils/index.ts +4 -1
  41. package/package.json +1 -1
  42. package/src/android/templates.ts +0 -2
  43. package/src/nuxt/nuxt-wizard.ts +23 -5
  44. package/src/nuxt/sdk-setup.ts +166 -30
  45. package/src/nuxt/templates.ts +15 -8
  46. package/src/nuxt/types.ts +8 -0
  47. package/src/nuxt/utils.ts +17 -7
  48. package/src/utils/clack-utils.ts +55 -6
  49. package/src/utils/package-json.ts +5 -0
  50. package/src/utils/package-manager.ts +66 -0
  51. package/test/nuxt/templates.test.ts +33 -6
@@ -1,10 +1,10 @@
1
1
  // @ts-expect-error - clack is ESM and TS complains about that. It works though
2
- import clack from '@clack/prompts';
2
+ import * as clack from '@clack/prompts';
3
3
  import * as Sentry from '@sentry/node';
4
4
  import chalk from 'chalk';
5
5
  import fs from 'fs';
6
6
  // @ts-expect-error - magicast is ESM and TS complains about that. It works though
7
- import { loadFile, generateCode, MagicastError } from 'magicast';
7
+ import { loadFile, generateCode } from 'magicast';
8
8
  // @ts-expect-error - magicast is ESM and TS complains about that. It works though
9
9
  import { addNuxtModule } from 'magicast/helpers';
10
10
  import path from 'path';
@@ -15,12 +15,19 @@ import {
15
15
  getSentryConfigContents,
16
16
  } from './templates';
17
17
  import {
18
- abort,
19
18
  abortIfCancelled,
19
+ askShouldAddPackageOverride,
20
+ askShouldInstallPackage,
20
21
  featureSelectionPrompt,
22
+ installPackage,
21
23
  isUsingTypeScript,
24
+ opn,
22
25
  } from '../utils/clack-utils';
23
26
  import { traceStep } from '../telemetry';
27
+ import { lt, SemVer } from 'semver';
28
+ import { PackageManager, PNPM } from '../utils/package-manager';
29
+ import { hasPackageInstalled, PackageDotJson } from '../utils/package-json';
30
+ import { deploymentPlatforms, DeploymentPlatform } from './types';
24
31
 
25
32
  const possibleNuxtConfig = [
26
33
  'nuxt.config.js',
@@ -54,10 +61,40 @@ export async function getNuxtConfig(): Promise<string> {
54
61
  return path.join(process.cwd(), configFile);
55
62
  }
56
63
 
64
+ export async function askDeploymentPlatform(): Promise<
65
+ DeploymentPlatform | symbol
66
+ > {
67
+ return await abortIfCancelled(
68
+ clack.select({
69
+ message: 'Please select your deployment platform.',
70
+ options: deploymentPlatforms.map((platform) => ({
71
+ value: platform,
72
+ label: `${platform.charAt(0).toUpperCase()}${platform.slice(1)}`,
73
+ })),
74
+ }),
75
+ );
76
+ }
77
+
57
78
  export async function addSDKModule(
58
79
  config: string,
59
80
  options: { org: string; project: string; url: string; selfHosted: boolean },
81
+ deploymentPlatform: DeploymentPlatform | symbol,
60
82
  ): Promise<void> {
83
+ const shouldTopLevelImport =
84
+ deploymentPlatform === 'vercel' || deploymentPlatform === 'netlify';
85
+
86
+ if (shouldTopLevelImport) {
87
+ clack.log.warn(
88
+ `Sentry needs to be initialized before the application starts. ${chalk.cyan(
89
+ `${deploymentPlatform
90
+ .charAt(0)
91
+ .toUpperCase()}${deploymentPlatform.slice(1)}`,
92
+ )} does not support this yet.\n\nWe will inject the Sentry server-side config at the top of your Nuxt server entry file instead.\n\nThis comes with some restrictions, for more info see:\n\n${chalk.underline(
93
+ 'https://docs.sentry.io/platforms/javascript/guides/nuxt/install/top-level-import/',
94
+ )} `,
95
+ );
96
+ }
97
+
61
98
  try {
62
99
  const mod = await loadFile(config);
63
100
 
@@ -67,6 +104,9 @@ export async function addSDKModule(
67
104
  project: options.project,
68
105
  ...(options.selfHosted && { url: options.url }),
69
106
  },
107
+ ...(shouldTopLevelImport && {
108
+ autoInjectServerSentry: 'top-level-import',
109
+ }),
70
110
  });
71
111
  addNuxtModule(mod, '@sentry/nuxt/module', 'sourcemap', {
72
112
  client: 'hidden',
@@ -80,33 +120,31 @@ export async function addSDKModule(
80
120
  `Added Sentry Nuxt Module to ${chalk.cyan(path.basename(config))}.`,
81
121
  );
82
122
  } catch (e: unknown) {
83
- // Cases where users spread options are not covered by magicast,
84
- // so we fall back to showing how to configure the nuxt config
85
- // manually.
86
- if (e instanceof MagicastError) {
87
- clack.log.warn(
88
- `Automatic configuration of ${chalk.cyan(
89
- path.basename(config),
90
- )} failed, please add the following settings:`,
91
- );
92
- // eslint-disable-next-line no-console
93
- console.log(`\n\n${getNuxtModuleFallbackTemplate(options)}\n\n`);
94
- } else {
95
- clack.log.error(
96
- 'Error while adding the Sentry Nuxt Module to the Nuxt config.',
97
- );
98
- clack.log.info(
99
- chalk.dim(
100
- typeof e === 'object' && e != null && 'toString' in e
101
- ? e.toString()
102
- : typeof e === 'string'
103
- ? e
104
- : 'Unknown error',
105
- ),
106
- );
107
- Sentry.captureException('Error while setting up the Nuxt SDK');
108
- await abort('Exiting Wizard');
109
- }
123
+ clack.log.error(
124
+ 'Error while adding the Sentry Nuxt Module to the Nuxt config.',
125
+ );
126
+ clack.log.info(
127
+ chalk.dim(
128
+ typeof e === 'object' && e != null && 'toString' in e
129
+ ? e.toString()
130
+ : typeof e === 'string'
131
+ ? e
132
+ : 'Unknown error',
133
+ ),
134
+ );
135
+ Sentry.captureException(
136
+ 'Error while setting up the Nuxt Module in nuxt config',
137
+ );
138
+
139
+ clack.log.warn(
140
+ `Please add the following settings to ${chalk.cyan(
141
+ path.basename(config),
142
+ )}:`,
143
+ );
144
+ // eslint-disable-next-line no-console
145
+ console.log(
146
+ `\n\n${getNuxtModuleFallbackTemplate(options, shouldTopLevelImport)}\n\n`,
147
+ );
110
148
  }
111
149
  }
112
150
 
@@ -207,3 +245,101 @@ export async function createConfigFiles(dsn: string) {
207
245
  });
208
246
  }
209
247
  }
248
+
249
+ export async function addNuxtOverrides(
250
+ packageJson: PackageDotJson,
251
+ packageManager: PackageManager,
252
+ nuxtMinVer: SemVer | null,
253
+ ) {
254
+ const isPNPM = PNPM.detect();
255
+
256
+ const overrides = [
257
+ {
258
+ pkgName: '@vercel/nft',
259
+ pkgVersion: '^0.27.4',
260
+ },
261
+ ...(nuxtMinVer && lt(nuxtMinVer, '3.14.0')
262
+ ? [{ pkgName: 'ofetch', pkgVersion: '^1.4.0' }]
263
+ : []),
264
+ ];
265
+
266
+ clack.log.warn(
267
+ `To ensure Sentry can properly instrument your code it needs to add version overrides for some Nuxt dependencies${
268
+ isPNPM ? ` and install ${chalk.cyan('import-in-the-middle')}.` : '.'
269
+ }\n\nFor more info see: ${chalk.underline(
270
+ 'https://github.com/getsentry/sentry-javascript/issues/14514',
271
+ )}${
272
+ isPNPM
273
+ ? `\n\nand ${chalk.underline(
274
+ 'https://docs.sentry.io/platforms/javascript/guides/nuxt/troubleshooting/#pnpm-dev-cannot-find-package-import-in-the-middle',
275
+ )}`
276
+ : ''
277
+ }`,
278
+ );
279
+
280
+ for (const { pkgName, pkgVersion } of overrides) {
281
+ const shouldAddOverride = await askShouldAddPackageOverride(
282
+ pkgName,
283
+ pkgVersion,
284
+ );
285
+
286
+ if (shouldAddOverride) {
287
+ await packageManager.addOverride(pkgName, pkgVersion);
288
+ }
289
+ }
290
+
291
+ if (PNPM.detect()) {
292
+ // For pnpm, we want to install iitm
293
+ // See: https://docs.sentry.io/platforms/javascript/guides/nuxt/troubleshooting/#pnpm-dev-cannot-find-package-import-in-the-middle
294
+ const iitmAlreadyInstalled = hasPackageInstalled(
295
+ 'import-in-the-middle',
296
+ packageJson,
297
+ );
298
+ Sentry.setTag('iitm-already-installed', iitmAlreadyInstalled);
299
+
300
+ const shouldInstallIitm = await askShouldInstallPackage(
301
+ 'import-in-the-middle',
302
+ );
303
+
304
+ if (shouldInstallIitm) {
305
+ await installPackage({
306
+ packageName: 'import-in-the-middle',
307
+ alreadyInstalled: iitmAlreadyInstalled,
308
+ packageManager,
309
+ });
310
+ }
311
+ }
312
+ }
313
+
314
+ export async function confirmReadImportDocs(
315
+ deploymentPlatform: DeploymentPlatform | symbol,
316
+ ) {
317
+ const canImportSentryServerConfigFile =
318
+ deploymentPlatform !== 'vercel' && deploymentPlatform !== 'netlify';
319
+
320
+ if (!canImportSentryServerConfigFile) {
321
+ // Nothing to do, users have been set up with automatic top-level-import instead
322
+ return;
323
+ }
324
+
325
+ const docsUrl =
326
+ 'https://docs.sentry.io/platforms/javascript/guides/nuxt/install/cli-import/#initializing-sentry-with---import';
327
+
328
+ clack.log.info(
329
+ `After building your Nuxt app, you need to ${chalk.bold(
330
+ '--import',
331
+ )} the Sentry server config file when running your app.\n\nFor more info, see:\n\n${chalk.underline(
332
+ docsUrl,
333
+ )}`,
334
+ );
335
+
336
+ const shouldOpenDocs = await abortIfCancelled(
337
+ clack.confirm({ message: 'Do you want to open the docs?' }),
338
+ );
339
+
340
+ if (shouldOpenDocs) {
341
+ opn(docsUrl, { wait: false }).catch(() => {
342
+ // opn throws in environments that don't have a browser (e.g. remote shells) so we just noop here
343
+ });
344
+ }
345
+ }
@@ -14,12 +14,15 @@ export default defineNuxtConfig({
14
14
  `;
15
15
  }
16
16
 
17
- export function getNuxtModuleFallbackTemplate(options: {
18
- org: string;
19
- project: string;
20
- url: string;
21
- selfHosted: boolean;
22
- }): string {
17
+ export function getNuxtModuleFallbackTemplate(
18
+ options: {
19
+ org: string;
20
+ project: string;
21
+ url: string;
22
+ selfHosted: boolean;
23
+ },
24
+ shouldTopLevelImport: boolean,
25
+ ): string {
23
26
  return ` modules: ["@sentry/nuxt/module"],
24
27
  sentry: {
25
28
  sourceMapsUploadOptions: {
@@ -27,7 +30,11 @@ export function getNuxtModuleFallbackTemplate(options: {
27
30
  project: "${options.project}",${
28
31
  options.selfHosted ? `\n url: "${options.url}",` : ''
29
32
  }
30
- },
33
+ },${
34
+ shouldTopLevelImport
35
+ ? `\n autoInjectServerSentry: "top-level-import",`
36
+ : ''
37
+ }
31
38
  },
32
39
  sourcemap: { client: "hidden" },`;
33
40
  }
@@ -239,7 +246,7 @@ export function getSentryExampleApiTemplate() {
239
246
  // Feel free to delete this file.
240
247
  import { defineEventHandler } from '#imports';
241
248
 
242
- export default defineEventHandler(event => {
249
+ export default defineEventHandler(() => {
243
250
  throw new Error("Sentry Example API Route Error");
244
251
  });
245
252
  `;
@@ -0,0 +1,8 @@
1
+ export const deploymentPlatforms = [
2
+ 'vercel',
3
+ 'netlify',
4
+ 'other',
5
+ 'none',
6
+ ] as const;
7
+
8
+ export type DeploymentPlatform = (typeof deploymentPlatforms)[number];
package/src/nuxt/utils.ts CHANGED
@@ -1,6 +1,9 @@
1
+ // @ts-ignore - clack is ESM and TS complains about that. It works though
2
+ import * as clack from '@clack/prompts';
1
3
  import { gte, minVersion } from 'semver';
2
4
  // @ts-expect-error - magicast is ESM and TS complains about that. It works though
3
5
  import { loadFile } from 'magicast';
6
+ import { abortIfCancelled } from '../utils/clack-utils';
4
7
 
5
8
  export async function isNuxtV4(
6
9
  nuxtConfig: string,
@@ -18,14 +21,21 @@ export async function isNuxtV4(
18
21
  // At the time of writing, nuxt 4 is not on its own
19
22
  // major yet. We must read the `compatibilityVersion`
20
23
  // from the nuxt config.
21
- const mod = await loadFile(nuxtConfig);
22
- const config =
23
- mod.exports.default.$type === 'function-call'
24
- ? mod.exports.default.$args[0]
25
- : mod.exports.default;
24
+ try {
25
+ const mod = await loadFile(nuxtConfig);
26
+ const config =
27
+ mod.exports.default.$type === 'function-call'
28
+ ? mod.exports.default.$args[0]
29
+ : mod.exports.default;
26
30
 
27
- if (config && config.future && config.future.compatibilityVersion === 4) {
28
- return true;
31
+ if (config && config.future && config.future.compatibilityVersion === 4) {
32
+ return true;
33
+ }
34
+ } catch {
35
+ // If we cannot parse their config, just ask.
36
+ return await abortIfCancelled(
37
+ clack.confirm({ message: 'Are you using Nuxt version 4?' }),
38
+ );
29
39
  }
30
40
 
31
41
  return false;
@@ -20,7 +20,7 @@ import {
20
20
  import { debug } from './debug';
21
21
  import { fulfillsVersionRange } from './semver';
22
22
 
23
- const opn = require('opn') as (
23
+ export const opn = require('opn') as (
24
24
  url: string,
25
25
  options?: {
26
26
  wait?: boolean;
@@ -355,6 +355,7 @@ export async function installPackage({
355
355
  alreadyInstalled,
356
356
  askBeforeUpdating = true,
357
357
  packageNameDisplayLabel,
358
+ packageManager,
358
359
  }: {
359
360
  /** The string that is passed to the package manager CLI as identifier to install (e.g. `@sentry/nextjs`, or `@sentry/nextjs@^8`) */
360
361
  packageName: string;
@@ -362,6 +363,7 @@ export async function installPackage({
362
363
  askBeforeUpdating?: boolean;
363
364
  /** Overrides what is shown in the installation logs in place of the `packageName` option. Useful if the `packageName` is ugly (e.g. `@sentry/nextjs@^8`) */
364
365
  packageNameDisplayLabel?: string;
366
+ packageManager?: PackageManager;
365
367
  }): Promise<{ packageManager?: PackageManager }> {
366
368
  return traceStep('install-package', async () => {
367
369
  if (alreadyInstalled && askBeforeUpdating) {
@@ -380,18 +382,18 @@ export async function installPackage({
380
382
 
381
383
  const sdkInstallSpinner = clack.spinner();
382
384
 
383
- const packageManager = await getPackageManager();
385
+ const pkgManager = packageManager || (await getPackageManager());
384
386
 
385
387
  sdkInstallSpinner.start(
386
388
  `${alreadyInstalled ? 'Updating' : 'Installing'} ${chalk.bold.cyan(
387
389
  packageNameDisplayLabel ?? packageName,
388
- )} with ${chalk.bold(packageManager.label)}.`,
390
+ )} with ${chalk.bold(pkgManager.label)}.`,
389
391
  );
390
392
 
391
393
  try {
392
394
  await new Promise<void>((resolve, reject) => {
393
395
  childProcess.exec(
394
- `${packageManager.installCommand} ${packageName} ${packageManager.flags}`,
396
+ `${pkgManager.installCommand} ${packageName} ${pkgManager.flags}`,
395
397
  (err, stdout, stderr) => {
396
398
  if (err) {
397
399
  // Write a log file so we can better troubleshoot issues
@@ -430,10 +432,10 @@ export async function installPackage({
430
432
  sdkInstallSpinner.stop(
431
433
  `${alreadyInstalled ? 'Updated' : 'Installed'} ${chalk.bold.cyan(
432
434
  packageNameDisplayLabel ?? packageName,
433
- )} with ${chalk.bold(packageManager.label)}.`,
435
+ )} with ${chalk.bold(pkgManager.label)}.`,
434
436
  );
435
437
 
436
- return { packageManager };
438
+ return { packageManager: pkgManager };
437
439
  });
438
440
  }
439
441
 
@@ -808,6 +810,26 @@ export async function getPackageDotJson(): Promise<PackageDotJson> {
808
810
  return packageJson || {};
809
811
  }
810
812
 
813
+ export async function updatePackageDotJson(
814
+ packageDotJson: PackageDotJson,
815
+ ): Promise<void> {
816
+ try {
817
+ await fs.promises.writeFile(
818
+ path.join(process.cwd(), 'package.json'),
819
+ // TODO: maybe figure out the original indentation
820
+ JSON.stringify(packageDotJson, null, 2),
821
+ {
822
+ encoding: 'utf8',
823
+ flag: 'w',
824
+ },
825
+ );
826
+ } catch {
827
+ clack.log.error(`Unable to update your ${chalk.cyan('package.json')}.`);
828
+
829
+ await abort();
830
+ }
831
+ }
832
+
811
833
  export async function getPackageManager(): Promise<PackageManager> {
812
834
  const detectedPackageManager = detectPackageManger();
813
835
 
@@ -1469,3 +1491,30 @@ export async function featureSelectionPrompt<F extends ReadonlyArray<Feature>>(
1469
1491
  return selectedFeatures as { [key in F[number]['id']]: boolean };
1470
1492
  });
1471
1493
  }
1494
+
1495
+ export async function askShouldInstallPackage(
1496
+ pkgName: string,
1497
+ ): Promise<boolean> {
1498
+ return traceStep(`ask-install-package`, () =>
1499
+ abortIfCancelled(
1500
+ clack.confirm({
1501
+ message: `Do you want to install ${chalk.cyan(pkgName)}?`,
1502
+ }),
1503
+ ),
1504
+ );
1505
+ }
1506
+
1507
+ export async function askShouldAddPackageOverride(
1508
+ pkgName: string,
1509
+ pkgVersion: string,
1510
+ ): Promise<boolean> {
1511
+ return traceStep(`ask-add-package-override`, () =>
1512
+ abortIfCancelled(
1513
+ clack.confirm({
1514
+ message: `Do you want to add an override for ${chalk.cyan(
1515
+ pkgName,
1516
+ )} version ${chalk.cyan(pkgVersion)}?`,
1517
+ }),
1518
+ ),
1519
+ );
1520
+ }
@@ -3,6 +3,11 @@ export type PackageDotJson = {
3
3
  scripts?: Record<string, string | undefined>;
4
4
  dependencies?: Record<string, string>;
5
5
  devDependencies?: Record<string, string>;
6
+ resolutions?: Record<string, string>;
7
+ overrides?: Record<string, string>;
8
+ pnpm?: {
9
+ overrides?: Record<string, string>;
10
+ };
6
11
  };
7
12
 
8
13
  type NpmPackage = {
@@ -4,6 +4,7 @@ import * as path from 'path';
4
4
 
5
5
  import * as Sentry from '@sentry/node';
6
6
  import { traceStep } from '../telemetry';
7
+ import { getPackageDotJson, updatePackageDotJson } from './clack-utils';
7
8
 
8
9
  export interface PackageManager {
9
10
  name: string;
@@ -15,6 +16,7 @@ export interface PackageManager {
15
16
  runScriptCommand: string;
16
17
  flags: string;
17
18
  detect: () => boolean;
19
+ addOverride: (pkgName: string, pkgVersion: string) => Promise<void>;
18
20
  }
19
21
 
20
22
  export const BUN: PackageManager = {
@@ -26,6 +28,18 @@ export const BUN: PackageManager = {
26
28
  runScriptCommand: 'bun run',
27
29
  flags: '',
28
30
  detect: () => fs.existsSync(path.join(process.cwd(), BUN.lockFile)),
31
+ addOverride: async (pkgName, pkgVersion): Promise<void> => {
32
+ const packageDotJson = await getPackageDotJson();
33
+ const overrides = packageDotJson.overrides || {};
34
+
35
+ await updatePackageDotJson({
36
+ ...packageDotJson,
37
+ overrides: {
38
+ ...overrides,
39
+ [pkgName]: pkgVersion,
40
+ },
41
+ });
42
+ },
29
43
  };
30
44
  export const YARN_V1: PackageManager = {
31
45
  name: 'yarn',
@@ -45,6 +59,18 @@ export const YARN_V1: PackageManager = {
45
59
  return false;
46
60
  }
47
61
  },
62
+ addOverride: async (pkgName, pkgVersion): Promise<void> => {
63
+ const packageDotJson = await getPackageDotJson();
64
+ const resolutions = packageDotJson.resolutions || {};
65
+
66
+ await updatePackageDotJson({
67
+ ...packageDotJson,
68
+ resolutions: {
69
+ ...resolutions,
70
+ [pkgName]: pkgVersion,
71
+ },
72
+ });
73
+ },
48
74
  };
49
75
  /** YARN V2/3/4 */
50
76
  export const YARN_V2: PackageManager = {
@@ -65,6 +91,18 @@ export const YARN_V2: PackageManager = {
65
91
  return false;
66
92
  }
67
93
  },
94
+ addOverride: async (pkgName, pkgVersion): Promise<void> => {
95
+ const packageDotJson = await getPackageDotJson();
96
+ const resolutions = packageDotJson.resolutions || {};
97
+
98
+ await updatePackageDotJson({
99
+ ...packageDotJson,
100
+ resolutions: {
101
+ ...resolutions,
102
+ [pkgName]: pkgVersion,
103
+ },
104
+ });
105
+ },
68
106
  };
69
107
  export const PNPM: PackageManager = {
70
108
  name: 'pnpm',
@@ -75,6 +113,22 @@ export const PNPM: PackageManager = {
75
113
  runScriptCommand: 'pnpm',
76
114
  flags: '--ignore-workspace-root-check',
77
115
  detect: () => fs.existsSync(path.join(process.cwd(), PNPM.lockFile)),
116
+ addOverride: async (pkgName, pkgVersion): Promise<void> => {
117
+ const packageDotJson = await getPackageDotJson();
118
+ const pnpm = packageDotJson.pnpm || {};
119
+ const overrides = pnpm.overrides || {};
120
+
121
+ await updatePackageDotJson({
122
+ ...packageDotJson,
123
+ pnpm: {
124
+ ...pnpm,
125
+ overrides: {
126
+ ...overrides,
127
+ [pkgName]: pkgVersion,
128
+ },
129
+ },
130
+ });
131
+ },
78
132
  };
79
133
  export const NPM: PackageManager = {
80
134
  name: 'npm',
@@ -85,6 +139,18 @@ export const NPM: PackageManager = {
85
139
  runScriptCommand: 'npm run',
86
140
  flags: '',
87
141
  detect: () => fs.existsSync(path.join(process.cwd(), NPM.lockFile)),
142
+ addOverride: async (pkgName, pkgVersion): Promise<void> => {
143
+ const packageDotJson = await getPackageDotJson();
144
+ const overrides = packageDotJson.overrides || {};
145
+
146
+ await updatePackageDotJson({
147
+ ...packageDotJson,
148
+ overrides: {
149
+ ...overrides,
150
+ [pkgName]: pkgVersion,
151
+ },
152
+ });
153
+ },
88
154
  };
89
155
 
90
156
  export const packageManagers = [BUN, YARN_V1, YARN_V2, PNPM, NPM];
@@ -206,12 +206,38 @@ describe('Nuxt code templates', () => {
206
206
 
207
207
  describe('getNuxtModuleFallbackTemplate', () => {
208
208
  it('generates configuration options for the nuxt config', () => {
209
- const template = getNuxtModuleFallbackTemplate({
210
- org: 'my-org',
211
- project: 'my-project',
212
- url: 'https://sentry.io',
213
- selfHosted: false,
214
- });
209
+ const template = getNuxtModuleFallbackTemplate(
210
+ {
211
+ org: 'my-org',
212
+ project: 'my-project',
213
+ url: 'https://sentry.io',
214
+ selfHosted: false,
215
+ },
216
+ false,
217
+ );
218
+
219
+ expect(template).toMatchInlineSnapshot(`
220
+ " modules: ["@sentry/nuxt/module"],
221
+ sentry: {
222
+ sourceMapsUploadOptions: {
223
+ org: "my-org",
224
+ project: "my-project",
225
+ },
226
+ },
227
+ sourcemap: { client: "hidden" },"
228
+ `);
229
+ });
230
+
231
+ it('generates configuration options for the nuxt config with top level import', () => {
232
+ const template = getNuxtModuleFallbackTemplate(
233
+ {
234
+ org: 'my-org',
235
+ project: 'my-project',
236
+ url: 'https://sentry.io',
237
+ selfHosted: false,
238
+ },
239
+ true,
240
+ );
215
241
 
216
242
  expect(template).toMatchInlineSnapshot(`
217
243
  " modules: ["@sentry/nuxt/module"],
@@ -220,6 +246,7 @@ describe('Nuxt code templates', () => {
220
246
  org: "my-org",
221
247
  project: "my-project",
222
248
  },
249
+ autoInjectServerSentry: "top-level-import",
223
250
  },
224
251
  sourcemap: { client: "hidden" },"
225
252
  `);