@sentry/wizard 6.1.2 → 6.3.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 (104) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/e2e-tests/tests/angular-17.test.js +5 -0
  3. package/dist/e2e-tests/tests/angular-17.test.js.map +1 -1
  4. package/dist/e2e-tests/tests/angular-19.test.js +5 -0
  5. package/dist/e2e-tests/tests/angular-19.test.js.map +1 -1
  6. package/dist/e2e-tests/tests/expo.test.js +9 -2
  7. package/dist/e2e-tests/tests/expo.test.js.map +1 -1
  8. package/dist/e2e-tests/tests/flutter.test.js +32 -4
  9. package/dist/e2e-tests/tests/flutter.test.js.map +1 -1
  10. package/dist/e2e-tests/tests/nextjs-14.test.js +4 -3
  11. package/dist/e2e-tests/tests/nextjs-14.test.js.map +1 -1
  12. package/dist/e2e-tests/tests/nextjs-15.test.js +17 -4
  13. package/dist/e2e-tests/tests/nextjs-15.test.js.map +1 -1
  14. package/dist/e2e-tests/tests/nuxt-3.test.js +9 -2
  15. package/dist/e2e-tests/tests/nuxt-3.test.js.map +1 -1
  16. package/dist/e2e-tests/tests/nuxt-4.test.js +9 -2
  17. package/dist/e2e-tests/tests/nuxt-4.test.js.map +1 -1
  18. package/dist/e2e-tests/tests/react-native.test.js +8 -1
  19. package/dist/e2e-tests/tests/react-native.test.js.map +1 -1
  20. package/dist/e2e-tests/tests/remix.test.js +19 -4
  21. package/dist/e2e-tests/tests/remix.test.js.map +1 -1
  22. package/dist/e2e-tests/tests/sveltekit.test.js +16 -2
  23. package/dist/e2e-tests/tests/sveltekit.test.js.map +1 -1
  24. package/dist/lib/Steps/Integrations/Electron.js +4 -0
  25. package/dist/lib/Steps/Integrations/Electron.js.map +1 -1
  26. package/dist/src/android/android-wizard.js +3 -0
  27. package/dist/src/android/android-wizard.js.map +1 -1
  28. package/dist/src/angular/angular-wizard.js +3 -0
  29. package/dist/src/angular/angular-wizard.js.map +1 -1
  30. package/dist/src/apple/apple-wizard.js +13 -0
  31. package/dist/src/apple/apple-wizard.js.map +1 -1
  32. package/dist/src/apple/code-tools.d.ts +1 -1
  33. package/dist/src/apple/code-tools.js +3 -3
  34. package/dist/src/apple/code-tools.js.map +1 -1
  35. package/dist/src/apple/inject-code-snippet.d.ts +2 -1
  36. package/dist/src/apple/inject-code-snippet.js +2 -2
  37. package/dist/src/apple/inject-code-snippet.js.map +1 -1
  38. package/dist/src/apple/templates.d.ts +2 -2
  39. package/dist/src/apple/templates.js +22 -6
  40. package/dist/src/apple/templates.js.map +1 -1
  41. package/dist/src/flutter/code-tools.d.ts +1 -0
  42. package/dist/src/flutter/code-tools.js +6 -0
  43. package/dist/src/flutter/code-tools.js.map +1 -1
  44. package/dist/src/flutter/flutter-wizard.js +3 -0
  45. package/dist/src/flutter/flutter-wizard.js.map +1 -1
  46. package/dist/src/flutter/templates.d.ts +1 -0
  47. package/dist/src/flutter/templates.js +5 -0
  48. package/dist/src/flutter/templates.js.map +1 -1
  49. package/dist/src/nextjs/nextjs-wizard.js +14 -7
  50. package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
  51. package/dist/src/nextjs/utils.d.ts +8 -0
  52. package/dist/src/nextjs/utils.js +36 -1
  53. package/dist/src/nextjs/utils.js.map +1 -1
  54. package/dist/src/nuxt/nuxt-wizard.js +3 -0
  55. package/dist/src/nuxt/nuxt-wizard.js.map +1 -1
  56. package/dist/src/react-native/expo-metro.d.ts +2 -2
  57. package/dist/src/react-native/expo-metro.js +32 -27
  58. package/dist/src/react-native/expo-metro.js.map +1 -1
  59. package/dist/src/react-native/metro.d.ts +4 -4
  60. package/dist/src/react-native/metro.js +39 -17
  61. package/dist/src/react-native/metro.js.map +1 -1
  62. package/dist/src/react-native/react-native-wizard.js +3 -0
  63. package/dist/src/react-native/react-native-wizard.js.map +1 -1
  64. package/dist/src/remix/codemods/root.d.ts +1 -0
  65. package/dist/src/remix/codemods/root.js +30 -2
  66. package/dist/src/remix/codemods/root.js.map +1 -1
  67. package/dist/src/remix/remix-wizard.d.ts +4 -0
  68. package/dist/src/remix/remix-wizard.js +7 -0
  69. package/dist/src/remix/remix-wizard.js.map +1 -1
  70. package/dist/src/sveltekit/sveltekit-wizard.js +3 -0
  71. package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
  72. package/dist/src/utils/clack/mcp-config.d.ts +7 -0
  73. package/dist/src/utils/clack/mcp-config.js +427 -0
  74. package/dist/src/utils/clack/mcp-config.js.map +1 -0
  75. package/dist/src/version.d.ts +1 -1
  76. package/dist/src/version.js +1 -1
  77. package/dist/src/version.js.map +1 -1
  78. package/dist/test/angular/angular-wizard.test.js +3 -0
  79. package/dist/test/angular/angular-wizard.test.js.map +1 -1
  80. package/dist/test/apple/code-tools.test.js +62 -14
  81. package/dist/test/apple/code-tools.test.js.map +1 -1
  82. package/dist/test/apple/templates.test.js +71 -2
  83. package/dist/test/apple/templates.test.js.map +1 -1
  84. package/dist/test/flutter/code-tools.test.js +1 -0
  85. package/dist/test/flutter/code-tools.test.js.map +1 -1
  86. package/dist/test/flutter/templates.test.js +31 -1
  87. package/dist/test/flutter/templates.test.js.map +1 -1
  88. package/dist/test/nextjs/wizard-double-wrap-prevention.test.d.ts +1 -0
  89. package/dist/test/nextjs/wizard-double-wrap-prevention.test.js +269 -0
  90. package/dist/test/nextjs/wizard-double-wrap-prevention.test.js.map +1 -0
  91. package/dist/test/nuxt/templates.test.js +3 -0
  92. package/dist/test/nuxt/templates.test.js.map +1 -1
  93. package/dist/test/react-native/expo-metro.test.js +3 -3
  94. package/dist/test/react-native/expo-metro.test.js.map +1 -1
  95. package/dist/test/react-native/metro.test.js +76 -15
  96. package/dist/test/react-native/metro.test.js.map +1 -1
  97. package/dist/test/remix/root.test.js +229 -0
  98. package/dist/test/remix/root.test.js.map +1 -1
  99. package/dist/test/sveltekit/templates.test.js +3 -0
  100. package/dist/test/sveltekit/templates.test.js.map +1 -1
  101. package/dist/test/utils/clack/mcp-config.test.d.ts +1 -0
  102. package/dist/test/utils/clack/mcp-config.test.js +520 -0
  103. package/dist/test/utils/clack/mcp-config.test.js.map +1 -0
  104. package/package.json +1 -9
@@ -1 +1 @@
1
- {"version":3,"file":"templates.test.js","sourceRoot":"","sources":["../../../test/apple/templates.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, it } from 'vitest';\nimport {\n getFastlaneSnippet,\n getObjcSnippet,\n getRunScriptTemplate,\n getSwiftSnippet,\n scriptInputPath,\n} from '../../src/apple/templates';\n\ndescribe('templates', () => {\n describe('getRunScriptTemplate', () => {\n const variations: {\n uploadSource: boolean;\n includeHomebrewPath: boolean;\n expectedScript: string;\n }[] = [\n {\n uploadSource: true,\n includeHomebrewPath: true,\n expectedScript: `# This script is responsible for uploading debug symbols and source context for Sentry.\nif [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\nfi\n\nif which sentry-cli >/dev/null; then\n export SENTRY_ORG=test-org\n export SENTRY_PROJECT=test-project\n ERROR=$(sentry-cli debug-files upload --include-sources \"$DWARF_DSYM_FOLDER_PATH\" 2>&1 >/dev/null)\n if [ ! $? -eq 0 ]; then\n echo \"warning: sentry-cli - $ERROR\"\n fi\nelse\n echo \"warning: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases\"\nfi\n`,\n },\n {\n uploadSource: true,\n includeHomebrewPath: false,\n expectedScript: `# This script is responsible for uploading debug symbols and source context for Sentry.\nif which sentry-cli >/dev/null; then\n export SENTRY_ORG=test-org\n export SENTRY_PROJECT=test-project\n ERROR=$(sentry-cli debug-files upload --include-sources \"$DWARF_DSYM_FOLDER_PATH\" 2>&1 >/dev/null)\n if [ ! $? -eq 0 ]; then\n echo \"warning: sentry-cli - $ERROR\"\n fi\nelse\n echo \"warning: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases\"\nfi\n`,\n },\n {\n uploadSource: false,\n includeHomebrewPath: true,\n expectedScript: `# This script is responsible for uploading debug symbols and source context for Sentry.\nif [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\nfi\n\nif which sentry-cli >/dev/null; then\n export SENTRY_ORG=test-org\n export SENTRY_PROJECT=test-project\n ERROR=$(sentry-cli debug-files upload \"$DWARF_DSYM_FOLDER_PATH\" 2>&1 >/dev/null)\n if [ ! $? -eq 0 ]; then\n echo \"warning: sentry-cli - $ERROR\"\n fi\nelse\n echo \"warning: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases\"\nfi\n`,\n },\n {\n uploadSource: false,\n includeHomebrewPath: false,\n expectedScript: `# This script is responsible for uploading debug symbols and source context for Sentry.\nif which sentry-cli >/dev/null; then\n export SENTRY_ORG=test-org\n export SENTRY_PROJECT=test-project\n ERROR=$(sentry-cli debug-files upload \"$DWARF_DSYM_FOLDER_PATH\" 2>&1 >/dev/null)\n if [ ! $? -eq 0 ]; then\n echo \"warning: sentry-cli - $ERROR\"\n fi\nelse\n echo \"warning: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases\"\nfi\n`,\n },\n ];\n\n for (const variation of variations) {\n describe(`uploadSource: ${variation.uploadSource.toString()} and includeHomebrewPath: ${variation.includeHomebrewPath.toString()}`, () => {\n it('should return the correct script', () => {\n // -- ct --\n const script = getRunScriptTemplate(\n 'test-org',\n 'test-project',\n variation.uploadSource,\n variation.includeHomebrewPath,\n );\n\n // -- Assert --\n expect(script).toBe(variation.expectedScript);\n });\n });\n }\n });\n\n describe('scriptInputPath', () => {\n it('should return the correct path', () => {\n expect(scriptInputPath).toBe(\n '\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}\"',\n );\n });\n });\n\n describe('getSwiftSnippet', () => {\n it('should return the correct snippet', () => {\n // -- Arrange --\n const snippet = getSwiftSnippet('test-dsn');\n\n // -- Assert --\n expect(snippet).toBe(\n ` SentrySDK.start { options in\n options.dsn = \"test-dsn\"\n options.debug = true // Enabled debug when first installing is always helpful\n\n // Adds IP for users.\n // For more information, visit: https://docs.sentry.io/platforms/apple/data-management/data-collected/\n options.sendDefaultPii = true\n\n // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.\n // We recommend adjusting this value in production.\n options.tracesSampleRate = 1.0\n\n // Configure profiling. Visit https://docs.sentry.io/platforms/apple/profiling/ to learn more.\n options.configureProfiling = {\n $0.sessionSampleRate = 1.0 // We recommend adjusting this value in production.\n $0.lifecycle = .trace\n }\n\n // Uncomment the following lines to add more data to your events\n // options.attachScreenshot = true // This adds a screenshot to the error events\n // options.attachViewHierarchy = true // This adds the view hierarchy to the error events\n }\n // Remove the next line after confirming that your Sentry integration is working.\n SentrySDK.capture(message: \"This app uses Sentry! :)\")\n`,\n );\n });\n });\n\n describe('getObjcSnippet', () => {\n it('should return the correct snippet', () => {\n // -- Arrange --\n const snippet = getObjcSnippet('test-dsn');\n\n // -- Assert --\n expect(snippet).toBe(\n ` [SentrySDK startWithConfigureOptions:^(SentryOptions * options) {\n options.dsn = @\"test-dsn\";\n options.debug = YES; // Enabled debug when first installing is always helpful\n\n // Adds IP for users.\n // For more information, visit: https://docs.sentry.io/platforms/apple/data-management/data-collected/\n options.sendDefaultPii = YES;\n\n // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.\n // We recommend adjusting this value in production.\n options.tracesSampleRate = @1.0;\n\n // Configure profiling. Visit https://docs.sentry.io/platforms/apple/profiling/ to learn more.\n options.configureProfiling = ^(SentryProfileOptions *profiling) {\n profiling.sessionSampleRate = 1.0; // We recommend adjusting this value in production.\n profiling.lifecycle = SentryProfilingLifecycleTrace;\n };\n\n //Uncomment the following lines to add more data to your events\n //options.attachScreenshot = YES; //This will add a screenshot to the error events\n //options.attachViewHierarchy = YES; //This will add the view hierarchy to the error events\n }];\n //Remove the next line after confirming that your Sentry integration is working.\n [SentrySDK captureMessage:@\"This app uses Sentry!\"];\n`,\n );\n });\n });\n\n describe('getFastlaneSnippet', () => {\n it('should return the correct snippet', () => {\n // -- Arrange --\n const snippet = getFastlaneSnippet('test-org', 'test-project');\n\n // -- Assert --\n expect(snippet).toBe(\n ` sentry_cli(\n org_slug: 'test-org',\n project_slug: 'test-project',\n include_sources: true\n )`,\n );\n });\n });\n});\n"]}
1
+ {"version":3,"file":"templates.test.js","sourceRoot":"","sources":["../../../test/apple/templates.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, it, vi } from 'vitest';\nimport {\n getFastlaneSnippet,\n getObjcSnippet,\n getRunScriptTemplate,\n getSwiftSnippet,\n scriptInputPath,\n} from '../../src/apple/templates';\n\nvi.mock('../../src/utils/clack/mcp-config', () => ({\n offerProjectScopedMcpConfig: vi.fn().mockResolvedValue(undefined),\n}));\n\ndescribe('templates', () => {\n describe('getRunScriptTemplate', () => {\n const variations: {\n uploadSource: boolean;\n includeHomebrewPath: boolean;\n expectedScript: string;\n }[] = [\n {\n uploadSource: true,\n includeHomebrewPath: true,\n expectedScript: `# This script is responsible for uploading debug symbols and source context for Sentry.\nif [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\nfi\n\nif which sentry-cli >/dev/null; then\n export SENTRY_ORG=test-org\n export SENTRY_PROJECT=test-project\n ERROR=$(sentry-cli debug-files upload --include-sources \"$DWARF_DSYM_FOLDER_PATH\" 2>&1 >/dev/null)\n if [ ! $? -eq 0 ]; then\n echo \"warning: sentry-cli - $ERROR\"\n fi\nelse\n echo \"warning: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases\"\nfi\n`,\n },\n {\n uploadSource: true,\n includeHomebrewPath: false,\n expectedScript: `# This script is responsible for uploading debug symbols and source context for Sentry.\nif which sentry-cli >/dev/null; then\n export SENTRY_ORG=test-org\n export SENTRY_PROJECT=test-project\n ERROR=$(sentry-cli debug-files upload --include-sources \"$DWARF_DSYM_FOLDER_PATH\" 2>&1 >/dev/null)\n if [ ! $? -eq 0 ]; then\n echo \"warning: sentry-cli - $ERROR\"\n fi\nelse\n echo \"warning: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases\"\nfi\n`,\n },\n {\n uploadSource: false,\n includeHomebrewPath: true,\n expectedScript: `# This script is responsible for uploading debug symbols and source context for Sentry.\nif [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\nfi\n\nif which sentry-cli >/dev/null; then\n export SENTRY_ORG=test-org\n export SENTRY_PROJECT=test-project\n ERROR=$(sentry-cli debug-files upload \"$DWARF_DSYM_FOLDER_PATH\" 2>&1 >/dev/null)\n if [ ! $? -eq 0 ]; then\n echo \"warning: sentry-cli - $ERROR\"\n fi\nelse\n echo \"warning: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases\"\nfi\n`,\n },\n {\n uploadSource: false,\n includeHomebrewPath: false,\n expectedScript: `# This script is responsible for uploading debug symbols and source context for Sentry.\nif which sentry-cli >/dev/null; then\n export SENTRY_ORG=test-org\n export SENTRY_PROJECT=test-project\n ERROR=$(sentry-cli debug-files upload \"$DWARF_DSYM_FOLDER_PATH\" 2>&1 >/dev/null)\n if [ ! $? -eq 0 ]; then\n echo \"warning: sentry-cli - $ERROR\"\n fi\nelse\n echo \"warning: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases\"\nfi\n`,\n },\n ];\n\n for (const variation of variations) {\n describe(`uploadSource: ${variation.uploadSource.toString()} and includeHomebrewPath: ${variation.includeHomebrewPath.toString()}`, () => {\n it('should return the correct script', () => {\n // -- ct --\n const script = getRunScriptTemplate(\n 'test-org',\n 'test-project',\n variation.uploadSource,\n variation.includeHomebrewPath,\n );\n\n // -- Assert --\n expect(script).toBe(variation.expectedScript);\n });\n });\n }\n });\n\n describe('scriptInputPath', () => {\n it('should return the correct path', () => {\n expect(scriptInputPath).toBe(\n '\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}\"',\n );\n });\n });\n\n describe('getSwiftSnippet', () => {\n it('should return the correct snippet', () => {\n // -- Arrange --\n const snippet = getSwiftSnippet('test-dsn', false);\n\n // -- Assert --\n expect(snippet).toBe(\n ` SentrySDK.start { options in\n options.dsn = \"test-dsn\"\n options.debug = true // Enabled debug when first installing is always helpful\n\n // Adds IP for users.\n // For more information, visit: https://docs.sentry.io/platforms/apple/data-management/data-collected/\n options.sendDefaultPii = true\n\n // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.\n // We recommend adjusting this value in production.\n options.tracesSampleRate = 1.0\n\n // Configure profiling. Visit https://docs.sentry.io/platforms/apple/profiling/ to learn more.\n options.configureProfiling = {\n $0.sessionSampleRate = 1.0 // We recommend adjusting this value in production.\n $0.lifecycle = .trace\n }\n\n // Uncomment the following lines to add more data to your events\n // options.attachScreenshot = true // This adds a screenshot to the error events\n // options.attachViewHierarchy = true // This adds the view hierarchy to the error events\n }\n // Remove the next line after confirming that your Sentry integration is working.\n SentrySDK.capture(message: \"This app uses Sentry! :)\")\n`,\n );\n });\n\n it('should return the correct snippet with logs enabled', () => {\n // -- Arrange --\n const snippet = getSwiftSnippet('test-dsn', true);\n\n // -- Assert --\n expect(snippet).toBe(\n ` SentrySDK.start { options in\n options.dsn = \"test-dsn\"\n options.debug = true // Enabled debug when first installing is always helpful\n\n // Adds IP for users.\n // For more information, visit: https://docs.sentry.io/platforms/apple/data-management/data-collected/\n options.sendDefaultPii = true\n\n // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.\n // We recommend adjusting this value in production.\n options.tracesSampleRate = 1.0\n\n // Configure profiling. Visit https://docs.sentry.io/platforms/apple/profiling/ to learn more.\n options.configureProfiling = {\n $0.sessionSampleRate = 1.0 // We recommend adjusting this value in production.\n $0.lifecycle = .trace\n }\n\n // Uncomment the following lines to add more data to your events\n // options.attachScreenshot = true // This adds a screenshot to the error events\n // options.attachViewHierarchy = true // This adds the view hierarchy to the error events\n \n // Enable experimental logging features\n options.experimental.enableLogs = true\n }\n // Remove the next line after confirming that your Sentry integration is working.\n SentrySDK.capture(message: \"This app uses Sentry! :)\")\n`,\n );\n });\n });\n\n describe('getObjcSnippet', () => {\n it('should return the correct snippet', () => {\n // -- Arrange --\n const snippet = getObjcSnippet('test-dsn', false);\n\n // -- Assert --\n expect(snippet).toBe(\n ` [SentrySDK startWithConfigureOptions:^(SentryOptions * options) {\n options.dsn = @\"test-dsn\";\n options.debug = YES; // Enabled debug when first installing is always helpful\n\n // Adds IP for users.\n // For more information, visit: https://docs.sentry.io/platforms/apple/data-management/data-collected/\n options.sendDefaultPii = YES;\n\n // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.\n // We recommend adjusting this value in production.\n options.tracesSampleRate = @1.0;\n\n // Configure profiling. Visit https://docs.sentry.io/platforms/apple/profiling/ to learn more.\n options.configureProfiling = ^(SentryProfileOptions *profiling) {\n profiling.sessionSampleRate = 1.0; // We recommend adjusting this value in production.\n profiling.lifecycle = SentryProfilingLifecycleTrace;\n };\n\n //Uncomment the following lines to add more data to your events\n //options.attachScreenshot = YES; //This will add a screenshot to the error events\n //options.attachViewHierarchy = YES; //This will add the view hierarchy to the error events\n }];\n //Remove the next line after confirming that your Sentry integration is working.\n [SentrySDK captureMessage:@\"This app uses Sentry!\"];\n`,\n );\n });\n\n it('should return the correct snippet with logs enabled', () => {\n // -- Arrange --\n const snippet = getObjcSnippet('test-dsn', true);\n\n // -- Assert --\n expect(snippet).toBe(\n ` [SentrySDK startWithConfigureOptions:^(SentryOptions * options) {\n options.dsn = @\"test-dsn\";\n options.debug = YES; // Enabled debug when first installing is always helpful\n\n // Adds IP for users.\n // For more information, visit: https://docs.sentry.io/platforms/apple/data-management/data-collected/\n options.sendDefaultPii = YES;\n\n // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.\n // We recommend adjusting this value in production.\n options.tracesSampleRate = @1.0;\n\n // Configure profiling. Visit https://docs.sentry.io/platforms/apple/profiling/ to learn more.\n options.configureProfiling = ^(SentryProfileOptions *profiling) {\n profiling.sessionSampleRate = 1.0; // We recommend adjusting this value in production.\n profiling.lifecycle = SentryProfilingLifecycleTrace;\n };\n\n //Uncomment the following lines to add more data to your events\n //options.attachScreenshot = YES; //This will add a screenshot to the error events\n //options.attachViewHierarchy = YES; //This will add the view hierarchy to the error events\n \n // Enable experimental logging features\n options.experimental.enableLogs = YES;\n }];\n //Remove the next line after confirming that your Sentry integration is working.\n [SentrySDK captureMessage:@\"This app uses Sentry!\"];\n`,\n );\n });\n });\n\n describe('getFastlaneSnippet', () => {\n it('should return the correct snippet', () => {\n // -- Arrange --\n const snippet = getFastlaneSnippet('test-org', 'test-project');\n\n // -- Assert --\n expect(snippet).toBe(\n ` sentry_cli(\n org_slug: 'test-org',\n project_slug: 'test-project',\n include_sources: true\n )`,\n );\n });\n });\n});\n"]}
@@ -36,6 +36,7 @@ void main() {
36
36
  tracing: true,
37
37
  profiling: true,
38
38
  replay: true,
39
+ logs: true,
39
40
  };
40
41
  const simpleRunAppPatched = `import 'package:flutter/widgets.dart';
41
42
  import 'package:sentry_flutter/sentry_flutter.dart';
@@ -1 +1 @@
1
- {"version":3,"file":"code-tools.test.js","sourceRoot":"","sources":["../../../test/flutter/code-tools.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, it } from 'vitest';\nimport {\n patchMainContent,\n getDependenciesLocation,\n getDevDependenciesLocation,\n getLastImportLineLocation,\n} from '../../src/flutter/code-tools';\nimport { initSnippet } from '../../src/flutter/templates';\n\ndescribe('code-tools', () => {\n const pubspec = `name: flutter_example\ndescription: An example flutter app.\nversion: 1.0.0\npublish_to: 'none' # Remove this line if you wish to publish to pub.dev\n\nenvironment:\n sdk: '>=2.17.0 <4.0.0'\n flutter: '>=3.0.0'\n\ndependencies:\n flutter:\n sdk: flutter\n\ndev_dependencies:\n flutter_lints: ^2.0.0\n`;\n\n const simpleRunApp = `import 'package:flutter/widgets.dart';\n\nvoid main() {\n runApp(const MyApp());\n}\n`;\n\n const asyncRunApp = `import 'package:flutter/widgets.dart';\n\nvoid main() {\n runApp(const MyApp());\n}\n`;\n\n const selectedFeaturesMap = {\n tracing: true,\n profiling: true,\n replay: true,\n };\n\n const simpleRunAppPatched = `import 'package:flutter/widgets.dart';\nimport 'package:sentry_flutter/sentry_flutter.dart';\n\nFuture<void> main() async {\n ${initSnippet('dsn', selectedFeaturesMap, 'const MyApp()')}\n}\n`;\n\n const paramRunApp = `import 'package:flutter/widgets.dart';\n\nFuture<void> main() async {\n await someFunction();\n runApp(MyApp(param: SomeParam()));\n await anotherFunction();\n}\n`;\n\n const paramRunAppPatched = `import 'package:flutter/widgets.dart';\nimport 'package:sentry_flutter/sentry_flutter.dart';\n\nFuture<void> main() async {\n await someFunction();\n ${initSnippet('dsn', selectedFeaturesMap, 'MyApp(param: SomeParam())')}\n await anotherFunction();\n}\n`;\n\n const multilineRunApp = `import 'package:flutter/widgets.dart';\n\nvoid main() {\n runApp(\n MyApp(\n param: Param(),\n multi: Another(1),\n line: await bites(the: \"dust\"),\n ),\n );\n anotherFunction();\n}\n`;\n\n const multilineRunAppPatched = `import 'package:flutter/widgets.dart';\nimport 'package:sentry_flutter/sentry_flutter.dart';\n\nFuture<void> main() async {\n ${initSnippet(\n 'dsn',\n selectedFeaturesMap,\n `\n MyApp(\n param: Param(),\n multi: Another(1),\n line: await bites(the: \"dust\"),\n ),\n `,\n )}\n anotherFunction();\n}\n`;\n\n describe('patchMainContent', () => {\n it('wraps simple runApp', () => {\n expect(patchMainContent('dsn', simpleRunApp, selectedFeaturesMap)).toBe(\n simpleRunAppPatched,\n );\n });\n\n it('wraps async runApp', () => {\n expect(patchMainContent('dsn', asyncRunApp, selectedFeaturesMap)).toBe(\n simpleRunAppPatched,\n );\n });\n\n it('wraps runApp with parameterized app', () => {\n expect(patchMainContent('dsn', paramRunApp, selectedFeaturesMap)).toBe(\n paramRunAppPatched,\n );\n });\n\n it('wraps multiline runApp', () => {\n expect(\n patchMainContent('dsn', multilineRunApp, selectedFeaturesMap),\n ).toBe(multilineRunAppPatched);\n });\n });\n\n describe('pubspec', () => {\n it('returns proper line index for dependencies', () => {\n expect(getDependenciesLocation(pubspec)).toBe(\n pubspec.indexOf(' flutter:\\n'),\n );\n });\n\n it('returns proper line index for dev-dependencies', () => {\n expect(getDevDependenciesLocation(pubspec)).toBe(\n pubspec.indexOf(' flutter_lints: ^2.0.0\\n'),\n );\n });\n });\n\n describe('getLastImportLineLocation', () => {\n it('returns proper line index', () => {\n const code =\n `import 'foo:bar';\\n` + `//<insert-location>\\n` + `class X {}`;\n expect(getLastImportLineLocation(code)).toBe(\n code.indexOf('//<insert-location>'),\n );\n });\n\n it('returns proper line index when alias import is used', () => {\n const code =\n `import 'package:my_library/utils.dart' as utils;\\n` +\n `//<insert-location>\\n` +\n `class X {}`;\n expect(getLastImportLineLocation(code)).toBe(\n code.indexOf('//<insert-location>'),\n );\n });\n\n it('returns proper line index when specific parts import is used', () => {\n const code =\n `import 'dart:math' show pi, sin;\\n` +\n `//<insert-location>\\n` +\n `class X {}`;\n expect(getLastImportLineLocation(code)).toBe(\n code.indexOf('//<insert-location>'),\n );\n });\n\n it('returns proper line index when hide import is used', () => {\n const code =\n `import 'dart:math' hide Random;\\n` +\n `//<insert-location>\\n` +\n `class X {}`;\n expect(getLastImportLineLocation(code)).toBe(\n code.indexOf('//<insert-location>'),\n );\n });\n\n it('returns proper line index when deferred import is used', () => {\n const code =\n `import 'package:my_library/large_library.dart' deferred as largeLibrary;\\n` +\n `//<insert-location>\\n` +\n `class X {}`;\n expect(getLastImportLineLocation(code)).toBe(\n code.indexOf('//<insert-location>'),\n );\n });\n\n it('returns proper line index when multiple imports (with newlines) are present', () => {\n const code =\n `import 'foo:bar';\\n` +\n `import 'package:my_library/utils.dart' as utils;\\n` +\n `import 'dart:math' show pi, sin;\\n` +\n `import 'dart:math' hide Random;\\n` +\n `\\n` +\n `import 'package:my_library/large_library.dart' deferred as largeLibrary;\\n` +\n `//<insert-location>\\n` +\n `class X {}`;\n expect(getLastImportLineLocation(code)).toBe(\n code.indexOf('//<insert-location>'),\n );\n });\n });\n});\n"]}
1
+ {"version":3,"file":"code-tools.test.js","sourceRoot":"","sources":["../../../test/flutter/code-tools.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, it } from 'vitest';\nimport {\n patchMainContent,\n getDependenciesLocation,\n getDevDependenciesLocation,\n getLastImportLineLocation,\n} from '../../src/flutter/code-tools';\nimport { initSnippet } from '../../src/flutter/templates';\n\ndescribe('code-tools', () => {\n const pubspec = `name: flutter_example\ndescription: An example flutter app.\nversion: 1.0.0\npublish_to: 'none' # Remove this line if you wish to publish to pub.dev\n\nenvironment:\n sdk: '>=2.17.0 <4.0.0'\n flutter: '>=3.0.0'\n\ndependencies:\n flutter:\n sdk: flutter\n\ndev_dependencies:\n flutter_lints: ^2.0.0\n`;\n\n const simpleRunApp = `import 'package:flutter/widgets.dart';\n\nvoid main() {\n runApp(const MyApp());\n}\n`;\n\n const asyncRunApp = `import 'package:flutter/widgets.dart';\n\nvoid main() {\n runApp(const MyApp());\n}\n`;\n\n const selectedFeaturesMap = {\n tracing: true,\n profiling: true,\n replay: true,\n logs: true,\n };\n\n const simpleRunAppPatched = `import 'package:flutter/widgets.dart';\nimport 'package:sentry_flutter/sentry_flutter.dart';\n\nFuture<void> main() async {\n ${initSnippet('dsn', selectedFeaturesMap, 'const MyApp()')}\n}\n`;\n\n const paramRunApp = `import 'package:flutter/widgets.dart';\n\nFuture<void> main() async {\n await someFunction();\n runApp(MyApp(param: SomeParam()));\n await anotherFunction();\n}\n`;\n\n const paramRunAppPatched = `import 'package:flutter/widgets.dart';\nimport 'package:sentry_flutter/sentry_flutter.dart';\n\nFuture<void> main() async {\n await someFunction();\n ${initSnippet('dsn', selectedFeaturesMap, 'MyApp(param: SomeParam())')}\n await anotherFunction();\n}\n`;\n\n const multilineRunApp = `import 'package:flutter/widgets.dart';\n\nvoid main() {\n runApp(\n MyApp(\n param: Param(),\n multi: Another(1),\n line: await bites(the: \"dust\"),\n ),\n );\n anotherFunction();\n}\n`;\n\n const multilineRunAppPatched = `import 'package:flutter/widgets.dart';\nimport 'package:sentry_flutter/sentry_flutter.dart';\n\nFuture<void> main() async {\n ${initSnippet(\n 'dsn',\n selectedFeaturesMap,\n `\n MyApp(\n param: Param(),\n multi: Another(1),\n line: await bites(the: \"dust\"),\n ),\n `,\n )}\n anotherFunction();\n}\n`;\n\n describe('patchMainContent', () => {\n it('wraps simple runApp', () => {\n expect(patchMainContent('dsn', simpleRunApp, selectedFeaturesMap)).toBe(\n simpleRunAppPatched,\n );\n });\n\n it('wraps async runApp', () => {\n expect(patchMainContent('dsn', asyncRunApp, selectedFeaturesMap)).toBe(\n simpleRunAppPatched,\n );\n });\n\n it('wraps runApp with parameterized app', () => {\n expect(patchMainContent('dsn', paramRunApp, selectedFeaturesMap)).toBe(\n paramRunAppPatched,\n );\n });\n\n it('wraps multiline runApp', () => {\n expect(\n patchMainContent('dsn', multilineRunApp, selectedFeaturesMap),\n ).toBe(multilineRunAppPatched);\n });\n });\n\n describe('pubspec', () => {\n it('returns proper line index for dependencies', () => {\n expect(getDependenciesLocation(pubspec)).toBe(\n pubspec.indexOf(' flutter:\\n'),\n );\n });\n\n it('returns proper line index for dev-dependencies', () => {\n expect(getDevDependenciesLocation(pubspec)).toBe(\n pubspec.indexOf(' flutter_lints: ^2.0.0\\n'),\n );\n });\n });\n\n describe('getLastImportLineLocation', () => {\n it('returns proper line index', () => {\n const code =\n `import 'foo:bar';\\n` + `//<insert-location>\\n` + `class X {}`;\n expect(getLastImportLineLocation(code)).toBe(\n code.indexOf('//<insert-location>'),\n );\n });\n\n it('returns proper line index when alias import is used', () => {\n const code =\n `import 'package:my_library/utils.dart' as utils;\\n` +\n `//<insert-location>\\n` +\n `class X {}`;\n expect(getLastImportLineLocation(code)).toBe(\n code.indexOf('//<insert-location>'),\n );\n });\n\n it('returns proper line index when specific parts import is used', () => {\n const code =\n `import 'dart:math' show pi, sin;\\n` +\n `//<insert-location>\\n` +\n `class X {}`;\n expect(getLastImportLineLocation(code)).toBe(\n code.indexOf('//<insert-location>'),\n );\n });\n\n it('returns proper line index when hide import is used', () => {\n const code =\n `import 'dart:math' hide Random;\\n` +\n `//<insert-location>\\n` +\n `class X {}`;\n expect(getLastImportLineLocation(code)).toBe(\n code.indexOf('//<insert-location>'),\n );\n });\n\n it('returns proper line index when deferred import is used', () => {\n const code =\n `import 'package:my_library/large_library.dart' deferred as largeLibrary;\\n` +\n `//<insert-location>\\n` +\n `class X {}`;\n expect(getLastImportLineLocation(code)).toBe(\n code.indexOf('//<insert-location>'),\n );\n });\n\n it('returns proper line index when multiple imports (with newlines) are present', () => {\n const code =\n `import 'foo:bar';\\n` +\n `import 'package:my_library/utils.dart' as utils;\\n` +\n `import 'dart:math' show pi, sin;\\n` +\n `import 'dart:math' hide Random;\\n` +\n `\\n` +\n `import 'package:my_library/large_library.dart' deferred as largeLibrary;\\n` +\n `//<insert-location>\\n` +\n `class X {}`;\n expect(getLastImportLineLocation(code)).toBe(\n code.indexOf('//<insert-location>'),\n );\n });\n });\n});\n"]}
@@ -2,6 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const vitest_1 = require("vitest");
4
4
  const templates_1 = require("../../src/flutter/templates");
5
+ vitest_1.vi.mock('../../src/utils/clack/mcp-config', () => ({
6
+ offerProjectScopedMcpConfig: vitest_1.vi.fn().mockResolvedValue(undefined),
7
+ }));
5
8
  (0, vitest_1.describe)('Flutter code templates', () => {
6
9
  (0, vitest_1.describe)('pubspec', () => {
7
10
  (0, vitest_1.it)('generates pubspec with project and org', () => {
@@ -28,6 +31,7 @@ const templates_1 = require("../../src/flutter/templates");
28
31
  tracing: true,
29
32
  profiling: true,
30
33
  replay: true,
34
+ logs: true,
31
35
  }, 'const MyApp()');
32
36
  (0, vitest_1.expect)(template).toMatchInlineSnapshot(`
33
37
  "await SentryFlutter.init(
@@ -36,6 +40,7 @@ const templates_1 = require("../../src/flutter/templates");
36
40
  // Adds request headers and IP for users, for more info visit:
37
41
  // https://docs.sentry.io/platforms/dart/guides/flutter/data-management/data-collected/
38
42
  options.sendDefaultPii = true;
43
+ options.enableLogs = true;
39
44
  // Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing.
40
45
  // We recommend adjusting this value in production.
41
46
  options.tracesSampleRate = 1.0;
@@ -57,6 +62,7 @@ const templates_1 = require("../../src/flutter/templates");
57
62
  tracing: true,
58
63
  profiling: false,
59
64
  replay: false,
65
+ logs: true,
60
66
  }, 'const MyApp()');
61
67
  (0, vitest_1.expect)(template).toMatchInlineSnapshot(`
62
68
  "await SentryFlutter.init(
@@ -65,6 +71,7 @@ const templates_1 = require("../../src/flutter/templates");
65
71
  // Adds request headers and IP for users, for more info visit:
66
72
  // https://docs.sentry.io/platforms/dart/guides/flutter/data-management/data-collected/
67
73
  options.sendDefaultPii = true;
74
+ options.enableLogs = true;
68
75
  // Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing.
69
76
  // We recommend adjusting this value in production.
70
77
  options.tracesSampleRate = 1.0;
@@ -75,11 +82,12 @@ const templates_1 = require("../../src/flutter/templates");
75
82
  await Sentry.captureException(StateError('This is a sample exception.'));"
76
83
  `);
77
84
  });
78
- (0, vitest_1.it)('generates Sentry config with tracing, profiling & replay disabled', () => {
85
+ (0, vitest_1.it)('generates Sentry config with tracing, profiling, replay and logs disabled', () => {
79
86
  const template = (0, templates_1.initSnippet)('my-dsn', {
80
87
  tracing: false,
81
88
  profiling: false,
82
89
  replay: false,
90
+ logs: false,
83
91
  }, 'const MyApp()');
84
92
  (0, vitest_1.expect)(template).toMatchInlineSnapshot(`
85
93
  "await SentryFlutter.init(
@@ -95,6 +103,28 @@ const templates_1 = require("../../src/flutter/templates");
95
103
  await Sentry.captureException(StateError('This is a sample exception.'));"
96
104
  `);
97
105
  });
106
+ (0, vitest_1.it)('generates Sentry config with only structured logs enabled', () => {
107
+ const template = (0, templates_1.initSnippet)('my-dsn', {
108
+ tracing: false,
109
+ profiling: false,
110
+ replay: false,
111
+ logs: true,
112
+ }, 'const MyApp()');
113
+ (0, vitest_1.expect)(template).toMatchInlineSnapshot(`
114
+ "await SentryFlutter.init(
115
+ (options) {
116
+ options.dsn = 'my-dsn';
117
+ // Adds request headers and IP for users, for more info visit:
118
+ // https://docs.sentry.io/platforms/dart/guides/flutter/data-management/data-collected/
119
+ options.sendDefaultPii = true;
120
+ options.enableLogs = true;
121
+ },
122
+ appRunner: () => runApp(SentryWidget(child: const MyApp())),
123
+ );
124
+ // TODO: Remove this line after sending the first sample event to sentry.
125
+ await Sentry.captureException(StateError('This is a sample exception.'));"
126
+ `);
127
+ });
98
128
  });
99
129
  });
100
130
  //# sourceMappingURL=templates.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"templates.test.js","sourceRoot":"","sources":["../../../test/flutter/templates.test.ts"],"names":[],"mappings":";;AAAA,mCAA8C;AAC9C,2DAIqC;AAErC,IAAA,iBAAQ,EAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,IAAA,iBAAQ,EAAC,SAAS,EAAE,GAAG,EAAE;QACvB,IAAA,WAAE,EAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,QAAQ,GAAG,IAAA,0BAAc,EAAC,iBAAiB,EAAE,aAAa,CAAC,CAAC;YAClE,IAAA,eAAM,EAAC,QAAQ,CAAC,CAAC,qBAAqB,CAAC;;;;;;;aAOhC,CAAC,CAAC;QACX,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,IAAA,iBAAQ,EAAC,mBAAmB,EAAE,GAAG,EAAE;QACjC,IAAA,WAAE,EAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,QAAQ,GAAG,IAAA,4BAAgB,EAAC,eAAe,CAAC,CAAC;YACnD,IAAA,eAAM,EAAC,QAAQ,CAAC,CAAC,qBAAqB,CAAC,4BAA4B,CAAC,CAAC;QACvE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,IAAA,iBAAQ,EAAC,MAAM,EAAE,GAAG,EAAE;QACpB,IAAA,WAAE,EAAC,mDAAmD,EAAE,GAAG,EAAE;YAC3D,MAAM,QAAQ,GAAG,IAAA,uBAAW,EAC1B,QAAQ,EACR;gBACE,OAAO,EAAE,IAAI;gBACb,SAAS,EAAE,IAAI;gBACf,MAAM,EAAE,IAAI;aACb,EACD,eAAe,CAChB,CAAC;YACF,IAAA,eAAM,EAAC,QAAQ,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;;;;;;;;;;;OAqBtC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,0DAA0D,EAAE,GAAG,EAAE;YAClE,MAAM,QAAQ,GAAG,IAAA,uBAAW,EAC1B,QAAQ,EACR;gBACE,OAAO,EAAE,IAAI;gBACb,SAAS,EAAE,KAAK;gBAChB,MAAM,EAAE,KAAK;aACd,EACD,eAAe,CAChB,CAAC;YACF,IAAA,eAAM,EAAC,QAAQ,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;;;;;OAetC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,mEAAmE,EAAE,GAAG,EAAE;YAC3E,MAAM,QAAQ,GAAG,IAAA,uBAAW,EAC1B,QAAQ,EACR;gBACE,OAAO,EAAE,KAAK;gBACd,SAAS,EAAE,KAAK;gBAChB,MAAM,EAAE,KAAK;aACd,EACD,eAAe,CAChB,CAAC;YACF,IAAA,eAAM,EAAC,QAAQ,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;;OAYtC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, expect, it } from 'vitest';\nimport {\n pubspecOptions,\n sentryProperties,\n initSnippet,\n} from '../../src/flutter/templates';\n\ndescribe('Flutter code templates', () => {\n describe('pubspec', () => {\n it('generates pubspec with project and org', () => {\n const template = pubspecOptions('fixture-project', 'fixture-org');\n expect(template).toMatchInlineSnapshot(`\n \"sentry:\n upload_debug_symbols: true\n upload_source_maps: true\n project: fixture-project\n org: fixture-org\n \"\n `);\n });\n });\n describe('sentry.properties', () => {\n it('generates sentry.properties with token', () => {\n const template = sentryProperties('fixture-token');\n expect(template).toMatchInlineSnapshot(`\"auth_token=fixture-token\"`);\n });\n });\n describe('init', () => {\n it('generates Sentry config with all features enabled', () => {\n const template = initSnippet(\n 'my-dsn',\n {\n tracing: true,\n profiling: true,\n replay: true,\n },\n 'const MyApp()',\n );\n expect(template).toMatchInlineSnapshot(`\n \"await SentryFlutter.init(\n (options) {\n options.dsn = 'my-dsn';\n // Adds request headers and IP for users, for more info visit:\n // https://docs.sentry.io/platforms/dart/guides/flutter/data-management/data-collected/\n options.sendDefaultPii = true;\n // Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing.\n // We recommend adjusting this value in production.\n options.tracesSampleRate = 1.0;\n // The sampling rate for profiling is relative to tracesSampleRate\n // Setting to 1.0 will profile 100% of sampled transactions:\n options.profilesSampleRate = 1.0;\n // Configure Session Replay\n options.replay.sessionSampleRate = 0.1;\n options.replay.onErrorSampleRate = 1.0;\n },\n appRunner: () => runApp(SentryWidget(child: const MyApp())),\n );\n // TODO: Remove this line after sending the first sample event to sentry.\n await Sentry.captureException(StateError('This is a sample exception.'));\"\n `);\n });\n\n it('generates Sentry config with profiling & replay disabled', () => {\n const template = initSnippet(\n 'my-dsn',\n {\n tracing: true,\n profiling: false,\n replay: false,\n },\n 'const MyApp()',\n );\n expect(template).toMatchInlineSnapshot(`\n \"await SentryFlutter.init(\n (options) {\n options.dsn = 'my-dsn';\n // Adds request headers and IP for users, for more info visit:\n // https://docs.sentry.io/platforms/dart/guides/flutter/data-management/data-collected/\n options.sendDefaultPii = true;\n // Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing.\n // We recommend adjusting this value in production.\n options.tracesSampleRate = 1.0;\n },\n appRunner: () => runApp(SentryWidget(child: const MyApp())),\n );\n // TODO: Remove this line after sending the first sample event to sentry.\n await Sentry.captureException(StateError('This is a sample exception.'));\"\n `);\n });\n\n it('generates Sentry config with tracing, profiling & replay disabled', () => {\n const template = initSnippet(\n 'my-dsn',\n {\n tracing: false,\n profiling: false,\n replay: false,\n },\n 'const MyApp()',\n );\n expect(template).toMatchInlineSnapshot(`\n \"await SentryFlutter.init(\n (options) {\n options.dsn = 'my-dsn';\n // Adds request headers and IP for users, for more info visit:\n // https://docs.sentry.io/platforms/dart/guides/flutter/data-management/data-collected/\n options.sendDefaultPii = true;\n },\n appRunner: () => runApp(SentryWidget(child: const MyApp())),\n );\n // TODO: Remove this line after sending the first sample event to sentry.\n await Sentry.captureException(StateError('This is a sample exception.'));\"\n `);\n });\n });\n});\n"]}
1
+ {"version":3,"file":"templates.test.js","sourceRoot":"","sources":["../../../test/flutter/templates.test.ts"],"names":[],"mappings":";;AAAA,mCAAkD;AAClD,2DAIqC;AAErC,WAAE,CAAC,IAAI,CAAC,kCAAkC,EAAE,GAAG,EAAE,CAAC,CAAC;IACjD,2BAA2B,EAAE,WAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC;CAClE,CAAC,CAAC,CAAC;AAEJ,IAAA,iBAAQ,EAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,IAAA,iBAAQ,EAAC,SAAS,EAAE,GAAG,EAAE;QACvB,IAAA,WAAE,EAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,QAAQ,GAAG,IAAA,0BAAc,EAAC,iBAAiB,EAAE,aAAa,CAAC,CAAC;YAClE,IAAA,eAAM,EAAC,QAAQ,CAAC,CAAC,qBAAqB,CAAC;;;;;;;aAOhC,CAAC,CAAC;QACX,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,IAAA,iBAAQ,EAAC,mBAAmB,EAAE,GAAG,EAAE;QACjC,IAAA,WAAE,EAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,QAAQ,GAAG,IAAA,4BAAgB,EAAC,eAAe,CAAC,CAAC;YACnD,IAAA,eAAM,EAAC,QAAQ,CAAC,CAAC,qBAAqB,CAAC,4BAA4B,CAAC,CAAC;QACvE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,IAAA,iBAAQ,EAAC,MAAM,EAAE,GAAG,EAAE;QACpB,IAAA,WAAE,EAAC,mDAAmD,EAAE,GAAG,EAAE;YAC3D,MAAM,QAAQ,GAAG,IAAA,uBAAW,EAC1B,QAAQ,EACR;gBACE,OAAO,EAAE,IAAI;gBACb,SAAS,EAAE,IAAI;gBACf,MAAM,EAAE,IAAI;gBACZ,IAAI,EAAE,IAAI;aACX,EACD,eAAe,CAChB,CAAC;YACF,IAAA,eAAM,EAAC,QAAQ,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;;;;;;;;;;;;OAsBtC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,0DAA0D,EAAE,GAAG,EAAE;YAClE,MAAM,QAAQ,GAAG,IAAA,uBAAW,EAC1B,QAAQ,EACR;gBACE,OAAO,EAAE,IAAI;gBACb,SAAS,EAAE,KAAK;gBAChB,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,IAAI;aACX,EACD,eAAe,CAChB,CAAC;YACF,IAAA,eAAM,EAAC,QAAQ,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;;;;;;OAgBtC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,2EAA2E,EAAE,GAAG,EAAE;YACnF,MAAM,QAAQ,GAAG,IAAA,uBAAW,EAC1B,QAAQ,EACR;gBACE,OAAO,EAAE,KAAK;gBACd,SAAS,EAAE,KAAK;gBAChB,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,KAAK;aACZ,EACD,eAAe,CAChB,CAAC;YACF,IAAA,eAAM,EAAC,QAAQ,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;;OAYtC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,2DAA2D,EAAE,GAAG,EAAE;YACnE,MAAM,QAAQ,GAAG,IAAA,uBAAW,EAC1B,QAAQ,EACR;gBACE,OAAO,EAAE,KAAK;gBACd,SAAS,EAAE,KAAK;gBAChB,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,IAAI;aACX,EACD,eAAe,CAChB,CAAC;YACF,IAAA,eAAM,EAAC,QAAQ,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;;;OAatC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, expect, it, vi } from 'vitest';\nimport {\n pubspecOptions,\n sentryProperties,\n initSnippet,\n} from '../../src/flutter/templates';\n\nvi.mock('../../src/utils/clack/mcp-config', () => ({\n offerProjectScopedMcpConfig: vi.fn().mockResolvedValue(undefined),\n}));\n\ndescribe('Flutter code templates', () => {\n describe('pubspec', () => {\n it('generates pubspec with project and org', () => {\n const template = pubspecOptions('fixture-project', 'fixture-org');\n expect(template).toMatchInlineSnapshot(`\n \"sentry:\n upload_debug_symbols: true\n upload_source_maps: true\n project: fixture-project\n org: fixture-org\n \"\n `);\n });\n });\n describe('sentry.properties', () => {\n it('generates sentry.properties with token', () => {\n const template = sentryProperties('fixture-token');\n expect(template).toMatchInlineSnapshot(`\"auth_token=fixture-token\"`);\n });\n });\n describe('init', () => {\n it('generates Sentry config with all features enabled', () => {\n const template = initSnippet(\n 'my-dsn',\n {\n tracing: true,\n profiling: true,\n replay: true,\n logs: true,\n },\n 'const MyApp()',\n );\n expect(template).toMatchInlineSnapshot(`\n \"await SentryFlutter.init(\n (options) {\n options.dsn = 'my-dsn';\n // Adds request headers and IP for users, for more info visit:\n // https://docs.sentry.io/platforms/dart/guides/flutter/data-management/data-collected/\n options.sendDefaultPii = true;\n options.enableLogs = true;\n // Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing.\n // We recommend adjusting this value in production.\n options.tracesSampleRate = 1.0;\n // The sampling rate for profiling is relative to tracesSampleRate\n // Setting to 1.0 will profile 100% of sampled transactions:\n options.profilesSampleRate = 1.0;\n // Configure Session Replay\n options.replay.sessionSampleRate = 0.1;\n options.replay.onErrorSampleRate = 1.0;\n },\n appRunner: () => runApp(SentryWidget(child: const MyApp())),\n );\n // TODO: Remove this line after sending the first sample event to sentry.\n await Sentry.captureException(StateError('This is a sample exception.'));\"\n `);\n });\n\n it('generates Sentry config with profiling & replay disabled', () => {\n const template = initSnippet(\n 'my-dsn',\n {\n tracing: true,\n profiling: false,\n replay: false,\n logs: true,\n },\n 'const MyApp()',\n );\n expect(template).toMatchInlineSnapshot(`\n \"await SentryFlutter.init(\n (options) {\n options.dsn = 'my-dsn';\n // Adds request headers and IP for users, for more info visit:\n // https://docs.sentry.io/platforms/dart/guides/flutter/data-management/data-collected/\n options.sendDefaultPii = true;\n options.enableLogs = true;\n // Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing.\n // We recommend adjusting this value in production.\n options.tracesSampleRate = 1.0;\n },\n appRunner: () => runApp(SentryWidget(child: const MyApp())),\n );\n // TODO: Remove this line after sending the first sample event to sentry.\n await Sentry.captureException(StateError('This is a sample exception.'));\"\n `);\n });\n\n it('generates Sentry config with tracing, profiling, replay and logs disabled', () => {\n const template = initSnippet(\n 'my-dsn',\n {\n tracing: false,\n profiling: false,\n replay: false,\n logs: false,\n },\n 'const MyApp()',\n );\n expect(template).toMatchInlineSnapshot(`\n \"await SentryFlutter.init(\n (options) {\n options.dsn = 'my-dsn';\n // Adds request headers and IP for users, for more info visit:\n // https://docs.sentry.io/platforms/dart/guides/flutter/data-management/data-collected/\n options.sendDefaultPii = true;\n },\n appRunner: () => runApp(SentryWidget(child: const MyApp())),\n );\n // TODO: Remove this line after sending the first sample event to sentry.\n await Sentry.captureException(StateError('This is a sample exception.'));\"\n `);\n });\n\n it('generates Sentry config with only structured logs enabled', () => {\n const template = initSnippet(\n 'my-dsn',\n {\n tracing: false,\n profiling: false,\n replay: false,\n logs: true,\n },\n 'const MyApp()',\n );\n expect(template).toMatchInlineSnapshot(`\n \"await SentryFlutter.init(\n (options) {\n options.dsn = 'my-dsn';\n // Adds request headers and IP for users, for more info visit:\n // https://docs.sentry.io/platforms/dart/guides/flutter/data-management/data-collected/\n options.sendDefaultPii = true;\n options.enableLogs = true;\n },\n appRunner: () => runApp(SentryWidget(child: const MyApp())),\n );\n // TODO: Remove this line after sending the first sample event to sentry.\n await Sentry.captureException(StateError('This is a sample exception.'));\"\n `);\n });\n });\n});\n"]}
@@ -0,0 +1,269 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ // @ts-expect-error - magicast is ESM and TS complains about that. It works though
5
+ const magicast_1 = require("magicast");
6
+ const templates_1 = require("../../src/nextjs/templates");
7
+ const utils_1 = require("../../src/nextjs/utils");
8
+ vitest_1.vi.mock('../../src/utils/clack/mcp-config', () => ({
9
+ offerProjectScopedMcpConfig: vitest_1.vi.fn().mockResolvedValue(undefined),
10
+ }));
11
+ (0, vitest_1.describe)('Next.js wizard double wrap prevention', () => {
12
+ const mockWithSentryConfigOptionsTemplate = (0, templates_1.getWithSentryConfigOptionsTemplate)({
13
+ orgSlug: 'test-org',
14
+ projectSlug: 'test-project',
15
+ selfHosted: false,
16
+ sentryUrl: 'https://sentry.io',
17
+ tunnelRoute: false,
18
+ });
19
+ (0, vitest_1.describe)('unwrapSentryConfigAst utility function', () => {
20
+ (0, vitest_1.describe)('AST-based expression unwrapping', () => {
21
+ (0, vitest_1.it)('keeps code without withSentryConfig', () => {
22
+ const mod = (0, magicast_1.parseModule)('export default nextConfig');
23
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
24
+ const originalAST = mod.exports.default.$ast;
25
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
26
+ const resultAST = (0, utils_1.unwrapSentryConfigAst)(originalAST);
27
+ (0, vitest_1.expect)(resultAST).toBe(originalAST);
28
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
29
+ const { code: exportDefaultCode } = (0, magicast_1.generateCode)({ $ast: resultAST });
30
+ (0, vitest_1.expect)(exportDefaultCode).toMatchInlineSnapshot(`"nextConfig"`);
31
+ });
32
+ (0, vitest_1.it)('should handle plain object literal exports', () => {
33
+ const mod = (0, magicast_1.parseModule)(`export default { nextConfig: { randomValue: true } }`);
34
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
35
+ const originalAST = mod.exports.default.$ast;
36
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
37
+ const resultAST = (0, utils_1.unwrapSentryConfigAst)(originalAST);
38
+ (0, vitest_1.expect)(resultAST).toBe(originalAST);
39
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
40
+ (0, vitest_1.expect)(resultAST.type).toBe('ObjectExpression');
41
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
42
+ (0, vitest_1.expect)(resultAST.properties).toHaveLength(1);
43
+ });
44
+ (0, vitest_1.it)('should unwrap withSentryConfig with options', () => {
45
+ const mod = (0, magicast_1.parseModule)('export default withSentryConfig(nextConfig, { org: "test" })');
46
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
47
+ const wrappedAst = mod.exports.default.$ast;
48
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
49
+ const resultAST = (0, utils_1.unwrapSentryConfigAst)(wrappedAst);
50
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
51
+ const { code: exportDefaultCode } = (0, magicast_1.generateCode)({ $ast: resultAST });
52
+ (0, vitest_1.expect)(exportDefaultCode).toMatchInlineSnapshot(`"nextConfig"`);
53
+ });
54
+ (0, vitest_1.it)('should unwrap withSentryConfig without options', () => {
55
+ const mod = (0, magicast_1.parseModule)('export default withSentryConfig(nextConfig)');
56
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
57
+ const wrappedAst = mod.exports.default.$ast;
58
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
59
+ const resultAST = (0, utils_1.unwrapSentryConfigAst)(wrappedAst);
60
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
61
+ const { code: exportDefaultCode } = (0, magicast_1.generateCode)({ $ast: resultAST });
62
+ (0, vitest_1.expect)(exportDefaultCode).toMatchInlineSnapshot(`"nextConfig"`);
63
+ });
64
+ (0, vitest_1.it)('should handle nested withSentryConfig calls', () => {
65
+ const mod = (0, magicast_1.parseModule)('export default withSentryConfig(withSentryConfig(nextConfig, { org: "inner" }), { org: "outer" })');
66
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
67
+ const wrappedAst = mod.exports.default.$ast;
68
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
69
+ const resultAST = (0, utils_1.unwrapSentryConfigAst)(wrappedAst);
70
+ // Should unwrap one level and return the inner withSentryConfig call
71
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
72
+ (0, vitest_1.expect)(resultAST.type).toBe('CallExpression');
73
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
74
+ (0, vitest_1.expect)(resultAST.callee.name).toBe('withSentryConfig');
75
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
76
+ const { code: exportDefaultCode } = (0, magicast_1.generateCode)({ $ast: resultAST });
77
+ (0, vitest_1.expect)(exportDefaultCode).toMatchInlineSnapshot(`"withSentryConfig(nextConfig, { org: "inner" })"`);
78
+ });
79
+ (0, vitest_1.it)('should handle complex expressions', () => {
80
+ const mod = (0, magicast_1.parseModule)('export default withSentryConfig(someComplexExpression.withMethods())');
81
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
82
+ const wrappedAst = mod.exports.default.$ast;
83
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
84
+ const resultAST = (0, utils_1.unwrapSentryConfigAst)(wrappedAst);
85
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
86
+ const { code: exportDefaultCode } = (0, magicast_1.generateCode)({ $ast: resultAST });
87
+ (0, vitest_1.expect)(exportDefaultCode).toMatchInlineSnapshot(`"someComplexExpression.withMethods()"`);
88
+ });
89
+ (0, vitest_1.it)('should handle object literals', () => {
90
+ const mod = (0, magicast_1.parseModule)('export default withSentryConfig({ next: "config", obj: { next: "nested" } }, { org: "test" })');
91
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
92
+ const wrappedAst = mod.exports.default.$ast;
93
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
94
+ const resultAST = (0, utils_1.unwrapSentryConfigAst)(wrappedAst);
95
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
96
+ const { code: exportDefaultCode } = (0, magicast_1.generateCode)({ $ast: resultAST });
97
+ (0, vitest_1.expect)(exportDefaultCode).toMatchInlineSnapshot(`"{ next: "config", obj: { next: "nested" } }"`);
98
+ });
99
+ (0, vitest_1.it)('should return unchanged if not a withSentryConfig call', () => {
100
+ const mod = (0, magicast_1.parseModule)('export default someOtherFunction(nextConfig)');
101
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
102
+ const originalAST = mod.exports.default.$ast;
103
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
104
+ const resultAST = (0, utils_1.unwrapSentryConfigAst)(originalAST);
105
+ (0, vitest_1.expect)(resultAST).toBe(originalAST);
106
+ });
107
+ });
108
+ });
109
+ (0, vitest_1.describe)('MJS/TS files', () => {
110
+ (0, vitest_1.it)('should unwrap existing withSentryConfig and re-wrap with new config using AST', () => {
111
+ const existingMjsContent = `import { withSentryConfig } from "@sentry/nextjs";
112
+
113
+ const nextConfig = {};
114
+
115
+ export default withSentryConfig(nextConfig, {
116
+ org: "old-org",
117
+ project: "old-project",
118
+ });`;
119
+ const mod = (0, magicast_1.parseModule)(existingMjsContent);
120
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
121
+ const originalAST = mod.exports.default.$ast;
122
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
123
+ const unwrappedAst = (0, utils_1.unwrapSentryConfigAst)(originalAST);
124
+ // Verify it returns the first argument (nextConfig identifier)
125
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
126
+ (0, vitest_1.expect)(unwrappedAst.type).toBe('Identifier');
127
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
128
+ (0, vitest_1.expect)(unwrappedAst.name).toBe('nextConfig');
129
+ // Create a fresh module to simulate the re-wrapping process
130
+ const freshMod = (0, magicast_1.parseModule)(`import { withSentryConfig } from "@sentry/nextjs";
131
+
132
+ const nextConfig = {};
133
+
134
+ export default nextConfig;`);
135
+ // Apply wrapping
136
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
137
+ freshMod.exports.default = (0, utils_1.wrapWithSentryConfig)(freshMod.exports.default, mockWithSentryConfigOptionsTemplate);
138
+ const newCode = freshMod.generate().code;
139
+ // Verify only one withSentryConfig call exists
140
+ const withSentryConfigMatches = newCode.match(/withSentryConfig\s*\(/g);
141
+ (0, vitest_1.expect)(withSentryConfigMatches).toHaveLength(1);
142
+ (0, vitest_1.expect)(newCode).toContain('test-org');
143
+ (0, vitest_1.expect)(newCode).toContain('test-project');
144
+ (0, vitest_1.expect)(newCode).not.toContain('old-org');
145
+ (0, vitest_1.expect)(newCode).not.toContain('old-project');
146
+ });
147
+ (0, vitest_1.it)('should handle complex nested configurations using AST', () => {
148
+ const existingMjsContent = `import { withSentryConfig } from "@sentry/nextjs";
149
+
150
+ const nextConfig = { experimental: { appDir: true } };
151
+
152
+ export default withSentryConfig(
153
+ withSentryConfig(nextConfig, { org: "nested-org" }),
154
+ { org: "outer-org" }
155
+ );`;
156
+ const mod = (0, magicast_1.parseModule)(existingMjsContent);
157
+ // First unwrap ----
158
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
159
+ const firstUnwrapAST = (0, utils_1.unwrapSentryConfigAst)(mod.exports.default.$ast);
160
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
161
+ (0, vitest_1.expect)(firstUnwrapAST.type).toBe('CallExpression');
162
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
163
+ (0, vitest_1.expect)(firstUnwrapAST.callee.name).toBe('withSentryConfig');
164
+ const { code: exportDefaultCode1 } = (0, magicast_1.generateCode)({
165
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
166
+ $ast: firstUnwrapAST,
167
+ });
168
+ (0, vitest_1.expect)(exportDefaultCode1).toMatchInlineSnapshot(`"withSentryConfig(nextConfig, { org: "nested-org" })"`);
169
+ // Second unwrap ----
170
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
171
+ const secondUnwrapAST = (0, utils_1.unwrapSentryConfigAst)(firstUnwrapAST);
172
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
173
+ (0, vitest_1.expect)(secondUnwrapAST.type).toBe('Identifier');
174
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
175
+ (0, vitest_1.expect)(secondUnwrapAST.name).toBe('nextConfig');
176
+ const { code: exportDefaultCode2 } = (0, magicast_1.generateCode)({
177
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
178
+ $ast: secondUnwrapAST,
179
+ });
180
+ (0, vitest_1.expect)(exportDefaultCode2).toMatchInlineSnapshot(`"nextConfig"`);
181
+ });
182
+ (0, vitest_1.it)('should handle simple export without existing withSentryConfig using AST', () => {
183
+ const simpleMjsContent = `const nextConfig = {};
184
+
185
+ export default nextConfig;`;
186
+ const mod = (0, magicast_1.parseModule)(simpleMjsContent);
187
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
188
+ const originalAST = mod.exports.default.$ast;
189
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
190
+ const unwrappedAst = (0, utils_1.unwrapSentryConfigAst)(originalAST);
191
+ (0, vitest_1.expect)(unwrappedAst).toBe(originalAST);
192
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
193
+ mod.exports.default = (0, utils_1.wrapWithSentryConfig)(mod.exports.default, mockWithSentryConfigOptionsTemplate);
194
+ const newCode = mod.generate().code;
195
+ // Should have exactly one withSentryConfig call
196
+ const withSentryConfigMatches = newCode.match(/withSentryConfig\s*\(/g);
197
+ (0, vitest_1.expect)(withSentryConfigMatches).toHaveLength(1);
198
+ (0, vitest_1.expect)(newCode).toMatchInlineSnapshot(`
199
+ "const nextConfig = {};
200
+
201
+ export default withSentryConfig(nextConfig, ${sentryOptionsSnapshot});"
202
+ `);
203
+ });
204
+ (0, vitest_1.it)('should handle withSentryConfig(nextConfig) without options using AST', () => {
205
+ const existingMjsContent = `import { withSentryConfig } from "@sentry/nextjs";
206
+
207
+ const nextConfig = {};
208
+
209
+ export default withSentryConfig(nextConfig);`;
210
+ const mod = (0, magicast_1.parseModule)(existingMjsContent);
211
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
212
+ const originalAST = mod.exports.default.$ast;
213
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
214
+ const unwrappedAst = (0, utils_1.unwrapSentryConfigAst)(originalAST);
215
+ // Verify it returns the first argument (nextConfig identifier)
216
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
217
+ (0, vitest_1.expect)(unwrappedAst.type).toBe('Identifier');
218
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
219
+ (0, vitest_1.expect)(unwrappedAst.name).toBe('nextConfig');
220
+ // Simulate the re-wrapping process
221
+ const freshMod = (0, magicast_1.parseModule)(`import { withSentryConfig } from "@sentry/nextjs";
222
+
223
+ const nextConfig = {};
224
+
225
+ export default nextConfig;`);
226
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
227
+ freshMod.exports.default = (0, utils_1.wrapWithSentryConfig)(freshMod.exports.default, mockWithSentryConfigOptionsTemplate);
228
+ const newCode = freshMod.generate().code;
229
+ // Should have exactly one withSentryConfig call
230
+ const withSentryConfigMatches = newCode.match(/withSentryConfig\s*\(/g);
231
+ (0, vitest_1.expect)(withSentryConfigMatches).toHaveLength(1);
232
+ (0, vitest_1.expect)(newCode).not.toContain('withSentryConfig(withSentryConfig(');
233
+ (0, vitest_1.expect)(newCode).toMatch(/withSentryConfig\s*\(\s*nextConfig\s*,/);
234
+ });
235
+ });
236
+ });
237
+ const sentryOptionsSnapshot = `{
238
+ // For all available options, see:
239
+ // https://www.npmjs.com/package/@sentry/webpack-plugin#options
240
+
241
+ org: "test-org",
242
+
243
+ project: "test-project",
244
+
245
+ // Only print logs for uploading source maps in CI
246
+ silent: !process.env.CI,
247
+
248
+ // For all available options, see:
249
+ // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
250
+
251
+ // Upload a larger set of source maps for prettier stack traces (increases build time)
252
+ widenClientFileUpload: true,
253
+
254
+ // Uncomment to route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
255
+ // This can increase your server load as well as your hosting bill.
256
+ // Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
257
+ // side errors will fail.
258
+ // tunnelRoute: "/monitoring",
259
+
260
+ // Automatically tree-shake Sentry logger statements to reduce bundle size
261
+ disableLogger: true,
262
+
263
+ // Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)
264
+ // See the following for more information:
265
+ // https://docs.sentry.io/product/crons/
266
+ // https://vercel.com/docs/cron-jobs
267
+ automaticVercelMonitors: true
268
+ }`;
269
+ //# sourceMappingURL=wizard-double-wrap-prevention.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wizard-double-wrap-prevention.test.js","sourceRoot":"","sources":["../../../test/nextjs/wizard-double-wrap-prevention.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it, expect, vi } from 'vitest';\n// @ts-expect-error - magicast is ESM and TS complains about that. It works though\nimport { generateCode, parseModule } from 'magicast';\nimport { getWithSentryConfigOptionsTemplate } from '../../src/nextjs/templates';\nimport {\n unwrapSentryConfigAst,\n wrapWithSentryConfig,\n} from '../../src/nextjs/utils';\n\nvi.mock('../../src/utils/clack/mcp-config', () => ({\n offerProjectScopedMcpConfig: vi.fn().mockResolvedValue(undefined),\n}));\n\ndescribe('Next.js wizard double wrap prevention', () => {\n const mockWithSentryConfigOptionsTemplate =\n getWithSentryConfigOptionsTemplate({\n orgSlug: 'test-org',\n projectSlug: 'test-project',\n selfHosted: false,\n sentryUrl: 'https://sentry.io',\n tunnelRoute: false,\n });\n\n describe('unwrapSentryConfigAst utility function', () => {\n describe('AST-based expression unwrapping', () => {\n it('keeps code without withSentryConfig', () => {\n const mod = parseModule('export default nextConfig');\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const originalAST = mod.exports.default.$ast;\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const resultAST = unwrapSentryConfigAst(originalAST);\n expect(resultAST).toBe(originalAST);\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const { code: exportDefaultCode } = generateCode({ $ast: resultAST });\n expect(exportDefaultCode).toMatchInlineSnapshot(`\"nextConfig\"`);\n });\n\n it('should handle plain object literal exports', () => {\n const mod = parseModule(\n `export default { nextConfig: { randomValue: true } }`,\n );\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const originalAST = mod.exports.default.$ast;\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const resultAST = unwrapSentryConfigAst(originalAST);\n\n expect(resultAST).toBe(originalAST);\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n expect(resultAST.type).toBe('ObjectExpression');\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n expect(resultAST.properties).toHaveLength(1);\n });\n\n it('should unwrap withSentryConfig with options', () => {\n const mod = parseModule(\n 'export default withSentryConfig(nextConfig, { org: \"test\" })',\n );\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const wrappedAst = mod.exports.default.$ast;\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const resultAST = unwrapSentryConfigAst(wrappedAst);\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const { code: exportDefaultCode } = generateCode({ $ast: resultAST });\n\n expect(exportDefaultCode).toMatchInlineSnapshot(`\"nextConfig\"`);\n });\n\n it('should unwrap withSentryConfig without options', () => {\n const mod = parseModule('export default withSentryConfig(nextConfig)');\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const wrappedAst = mod.exports.default.$ast;\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const resultAST = unwrapSentryConfigAst(wrappedAst);\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const { code: exportDefaultCode } = generateCode({ $ast: resultAST });\n\n expect(exportDefaultCode).toMatchInlineSnapshot(`\"nextConfig\"`);\n });\n\n it('should handle nested withSentryConfig calls', () => {\n const mod = parseModule(\n 'export default withSentryConfig(withSentryConfig(nextConfig, { org: \"inner\" }), { org: \"outer\" })',\n );\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const wrappedAst = mod.exports.default.$ast;\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const resultAST = unwrapSentryConfigAst(wrappedAst);\n\n // Should unwrap one level and return the inner withSentryConfig call\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n expect(resultAST.type).toBe('CallExpression');\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n expect(resultAST.callee.name).toBe('withSentryConfig');\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const { code: exportDefaultCode } = generateCode({ $ast: resultAST });\n\n expect(exportDefaultCode).toMatchInlineSnapshot(\n `\"withSentryConfig(nextConfig, { org: \"inner\" })\"`,\n );\n });\n\n it('should handle complex expressions', () => {\n const mod = parseModule(\n 'export default withSentryConfig(someComplexExpression.withMethods())',\n );\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const wrappedAst = mod.exports.default.$ast;\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const resultAST = unwrapSentryConfigAst(wrappedAst);\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const { code: exportDefaultCode } = generateCode({ $ast: resultAST });\n\n expect(exportDefaultCode).toMatchInlineSnapshot(\n `\"someComplexExpression.withMethods()\"`,\n );\n });\n\n it('should handle object literals', () => {\n const mod = parseModule(\n 'export default withSentryConfig({ next: \"config\", obj: { next: \"nested\" } }, { org: \"test\" })',\n );\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const wrappedAst = mod.exports.default.$ast;\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const resultAST = unwrapSentryConfigAst(wrappedAst);\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const { code: exportDefaultCode } = generateCode({ $ast: resultAST });\n\n expect(exportDefaultCode).toMatchInlineSnapshot(\n `\"{ next: \"config\", obj: { next: \"nested\" } }\"`,\n );\n });\n\n it('should return unchanged if not a withSentryConfig call', () => {\n const mod = parseModule('export default someOtherFunction(nextConfig)');\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const originalAST = mod.exports.default.$ast;\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const resultAST = unwrapSentryConfigAst(originalAST);\n\n expect(resultAST).toBe(originalAST);\n });\n });\n });\n\n describe('MJS/TS files', () => {\n it('should unwrap existing withSentryConfig and re-wrap with new config using AST', () => {\n const existingMjsContent = `import { withSentryConfig } from \"@sentry/nextjs\";\n\nconst nextConfig = {};\n\nexport default withSentryConfig(nextConfig, {\n org: \"old-org\",\n project: \"old-project\",\n});`;\n\n const mod = parseModule(existingMjsContent);\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const originalAST = mod.exports.default.$ast;\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const unwrappedAst = unwrapSentryConfigAst(originalAST);\n // Verify it returns the first argument (nextConfig identifier)\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n expect(unwrappedAst.type).toBe('Identifier');\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n expect(unwrappedAst.name).toBe('nextConfig');\n\n // Create a fresh module to simulate the re-wrapping process\n const freshMod =\n parseModule(`import { withSentryConfig } from \"@sentry/nextjs\";\n\nconst nextConfig = {};\n\nexport default nextConfig;`);\n\n // Apply wrapping\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n freshMod.exports.default = wrapWithSentryConfig(\n freshMod.exports.default,\n mockWithSentryConfigOptionsTemplate,\n );\n\n const newCode = freshMod.generate().code;\n\n // Verify only one withSentryConfig call exists\n const withSentryConfigMatches = newCode.match(/withSentryConfig\\s*\\(/g);\n expect(withSentryConfigMatches).toHaveLength(1);\n\n expect(newCode).toContain('test-org');\n expect(newCode).toContain('test-project');\n expect(newCode).not.toContain('old-org');\n expect(newCode).not.toContain('old-project');\n });\n\n it('should handle complex nested configurations using AST', () => {\n const existingMjsContent = `import { withSentryConfig } from \"@sentry/nextjs\";\n\nconst nextConfig = { experimental: { appDir: true } };\n\nexport default withSentryConfig(\n withSentryConfig(nextConfig, { org: \"nested-org\" }),\n { org: \"outer-org\" }\n);`;\n\n const mod = parseModule(existingMjsContent);\n\n // First unwrap ----\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const firstUnwrapAST = unwrapSentryConfigAst(mod.exports.default.$ast);\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n expect(firstUnwrapAST.type).toBe('CallExpression');\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n expect(firstUnwrapAST.callee.name).toBe('withSentryConfig');\n\n const { code: exportDefaultCode1 } = generateCode({\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n $ast: firstUnwrapAST,\n });\n expect(exportDefaultCode1).toMatchInlineSnapshot(\n `\"withSentryConfig(nextConfig, { org: \"nested-org\" })\"`,\n );\n\n // Second unwrap ----\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const secondUnwrapAST = unwrapSentryConfigAst(firstUnwrapAST);\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n expect(secondUnwrapAST.type).toBe('Identifier');\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n expect(secondUnwrapAST.name).toBe('nextConfig');\n\n const { code: exportDefaultCode2 } = generateCode({\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n $ast: secondUnwrapAST,\n });\n\n expect(exportDefaultCode2).toMatchInlineSnapshot(`\"nextConfig\"`);\n });\n\n it('should handle simple export without existing withSentryConfig using AST', () => {\n const simpleMjsContent = `const nextConfig = {};\n\nexport default nextConfig;`;\n\n const mod = parseModule(simpleMjsContent);\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const originalAST = mod.exports.default.$ast;\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const unwrappedAst = unwrapSentryConfigAst(originalAST);\n expect(unwrappedAst).toBe(originalAST);\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n mod.exports.default = wrapWithSentryConfig(\n mod.exports.default,\n mockWithSentryConfigOptionsTemplate,\n );\n\n const newCode = mod.generate().code;\n\n // Should have exactly one withSentryConfig call\n const withSentryConfigMatches = newCode.match(/withSentryConfig\\s*\\(/g);\n expect(withSentryConfigMatches).toHaveLength(1);\n\n expect(newCode).toMatchInlineSnapshot(`\n \"const nextConfig = {};\n\n export default withSentryConfig(nextConfig, ${sentryOptionsSnapshot});\"\n `);\n });\n\n it('should handle withSentryConfig(nextConfig) without options using AST', () => {\n const existingMjsContent = `import { withSentryConfig } from \"@sentry/nextjs\";\n\nconst nextConfig = {};\n\nexport default withSentryConfig(nextConfig);`;\n\n const mod = parseModule(existingMjsContent);\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access\n const originalAST = mod.exports.default.$ast;\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const unwrappedAst = unwrapSentryConfigAst(originalAST);\n // Verify it returns the first argument (nextConfig identifier)\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n expect(unwrappedAst.type).toBe('Identifier');\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n expect(unwrappedAst.name).toBe('nextConfig');\n\n // Simulate the re-wrapping process\n const freshMod =\n parseModule(`import { withSentryConfig } from \"@sentry/nextjs\";\n\nconst nextConfig = {};\n\nexport default nextConfig;`);\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n freshMod.exports.default = wrapWithSentryConfig(\n freshMod.exports.default,\n mockWithSentryConfigOptionsTemplate,\n );\n\n const newCode = freshMod.generate().code;\n\n // Should have exactly one withSentryConfig call\n const withSentryConfigMatches = newCode.match(/withSentryConfig\\s*\\(/g);\n expect(withSentryConfigMatches).toHaveLength(1);\n\n expect(newCode).not.toContain('withSentryConfig(withSentryConfig(');\n expect(newCode).toMatch(/withSentryConfig\\s*\\(\\s*nextConfig\\s*,/);\n });\n });\n});\n\nconst sentryOptionsSnapshot = `{\n // For all available options, see:\n // https://www.npmjs.com/package/@sentry/webpack-plugin#options\n\n org: \"test-org\",\n\n project: \"test-project\",\n\n // Only print logs for uploading source maps in CI\n silent: !process.env.CI,\n\n // For all available options, see:\n // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/\n\n // Upload a larger set of source maps for prettier stack traces (increases build time)\n widenClientFileUpload: true,\n\n // Uncomment to route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.\n // This can increase your server load as well as your hosting bill.\n // Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-\n // side errors will fail.\n // tunnelRoute: \"/monitoring\",\n\n // Automatically tree-shake Sentry logger statements to reduce bundle size\n disableLogger: true,\n\n // Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)\n // See the following for more information:\n // https://docs.sentry.io/product/crons/\n // https://vercel.com/docs/cron-jobs\n automaticVercelMonitors: true\n}`;\n"]}
@@ -2,6 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const vitest_1 = require("vitest");
4
4
  const templates_1 = require("../../src/nuxt/templates");
5
+ vitest_1.vi.mock('../../src/utils/clack/mcp-config', () => ({
6
+ offerProjectScopedMcpConfig: vitest_1.vi.fn().mockResolvedValue(undefined),
7
+ }));
5
8
  (0, vitest_1.describe)('Nuxt code templates', () => {
6
9
  (0, vitest_1.describe)('getDefaultNuxtConfig', () => {
7
10
  (0, vitest_1.it)('returns a default nuxt config', () => {