@sentry/wizard 6.1.2 → 6.2.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 (59) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/e2e-tests/tests/flutter.test.js +16 -2
  3. package/dist/e2e-tests/tests/flutter.test.js.map +1 -1
  4. package/dist/e2e-tests/tests/remix.test.js +3 -2
  5. package/dist/e2e-tests/tests/remix.test.js.map +1 -1
  6. package/dist/lib/Steps/Integrations/Electron.js +4 -0
  7. package/dist/lib/Steps/Integrations/Electron.js.map +1 -1
  8. package/dist/src/apple/apple-wizard.js +10 -0
  9. package/dist/src/apple/apple-wizard.js.map +1 -1
  10. package/dist/src/apple/code-tools.d.ts +1 -1
  11. package/dist/src/apple/code-tools.js +3 -3
  12. package/dist/src/apple/code-tools.js.map +1 -1
  13. package/dist/src/apple/inject-code-snippet.d.ts +2 -1
  14. package/dist/src/apple/inject-code-snippet.js +2 -2
  15. package/dist/src/apple/inject-code-snippet.js.map +1 -1
  16. package/dist/src/apple/templates.d.ts +2 -2
  17. package/dist/src/apple/templates.js +22 -6
  18. package/dist/src/apple/templates.js.map +1 -1
  19. package/dist/src/flutter/code-tools.d.ts +1 -0
  20. package/dist/src/flutter/code-tools.js +6 -0
  21. package/dist/src/flutter/code-tools.js.map +1 -1
  22. package/dist/src/flutter/templates.d.ts +1 -0
  23. package/dist/src/flutter/templates.js +5 -0
  24. package/dist/src/flutter/templates.js.map +1 -1
  25. package/dist/src/nextjs/nextjs-wizard.js +11 -7
  26. package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
  27. package/dist/src/nextjs/utils.d.ts +8 -0
  28. package/dist/src/nextjs/utils.js +36 -1
  29. package/dist/src/nextjs/utils.js.map +1 -1
  30. package/dist/src/react-native/expo-metro.d.ts +2 -2
  31. package/dist/src/react-native/expo-metro.js +32 -27
  32. package/dist/src/react-native/expo-metro.js.map +1 -1
  33. package/dist/src/react-native/metro.d.ts +4 -4
  34. package/dist/src/react-native/metro.js +39 -17
  35. package/dist/src/react-native/metro.js.map +1 -1
  36. package/dist/src/remix/codemods/root.d.ts +1 -0
  37. package/dist/src/remix/codemods/root.js +30 -2
  38. package/dist/src/remix/codemods/root.js.map +1 -1
  39. package/dist/src/version.d.ts +1 -1
  40. package/dist/src/version.js +1 -1
  41. package/dist/src/version.js.map +1 -1
  42. package/dist/test/apple/code-tools.test.js +62 -14
  43. package/dist/test/apple/code-tools.test.js.map +1 -1
  44. package/dist/test/apple/templates.test.js +68 -2
  45. package/dist/test/apple/templates.test.js.map +1 -1
  46. package/dist/test/flutter/code-tools.test.js +1 -0
  47. package/dist/test/flutter/code-tools.test.js.map +1 -1
  48. package/dist/test/flutter/templates.test.js +28 -1
  49. package/dist/test/flutter/templates.test.js.map +1 -1
  50. package/dist/test/nextjs/wizard-double-wrap-prevention.test.d.ts +1 -0
  51. package/dist/test/nextjs/wizard-double-wrap-prevention.test.js +266 -0
  52. package/dist/test/nextjs/wizard-double-wrap-prevention.test.js.map +1 -0
  53. package/dist/test/react-native/expo-metro.test.js +3 -3
  54. package/dist/test/react-native/expo-metro.test.js.map +1 -1
  55. package/dist/test/react-native/metro.test.js +73 -15
  56. package/dist/test/react-native/metro.test.js.map +1 -1
  57. package/dist/test/remix/root.test.js +226 -0
  58. package/dist/test/remix/root.test.js.map +1 -1
  59. package/package.json +1 -9
@@ -1 +1 @@
1
- {"version":3,"file":"templates.test.js","sourceRoot":"","sources":["../../../test/apple/templates.test.ts"],"names":[],"mappings":";;AAAA,mCAA8C;AAC9C,yDAMmC;AAEnC,IAAA,iBAAQ,EAAC,WAAW,EAAE,GAAG,EAAE;IACzB,IAAA,iBAAQ,EAAC,sBAAsB,EAAE,GAAG,EAAE;QACpC,MAAM,UAAU,GAIV;YACJ;gBACE,YAAY,EAAE,IAAI;gBAClB,mBAAmB,EAAE,IAAI;gBACzB,cAAc,EAAE;;;;;;;;;;;;;;;CAevB;aACM;YACD;gBACE,YAAY,EAAE,IAAI;gBAClB,mBAAmB,EAAE,KAAK;gBAC1B,cAAc,EAAE;;;;;;;;;;;CAWvB;aACM;YACD;gBACE,YAAY,EAAE,KAAK;gBACnB,mBAAmB,EAAE,IAAI;gBACzB,cAAc,EAAE;;;;;;;;;;;;;;;CAevB;aACM;YACD;gBACE,YAAY,EAAE,KAAK;gBACnB,mBAAmB,EAAE,KAAK;gBAC1B,cAAc,EAAE;;;;;;;;;;;CAWvB;aACM;SACF,CAAC;QAEF,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE;YAClC,IAAA,iBAAQ,EAAC,iBAAiB,SAAS,CAAC,YAAY,CAAC,QAAQ,EAAE,6BAA6B,SAAS,CAAC,mBAAmB,CAAC,QAAQ,EAAE,EAAE,EAAE,GAAG,EAAE;gBACvI,IAAA,WAAE,EAAC,kCAAkC,EAAE,GAAG,EAAE;oBAC1C,WAAW;oBACX,MAAM,MAAM,GAAG,IAAA,gCAAoB,EACjC,UAAU,EACV,cAAc,EACd,SAAS,CAAC,YAAY,EACtB,SAAS,CAAC,mBAAmB,CAC9B,CAAC;oBAEF,eAAe;oBACf,IAAA,eAAM,EAAC,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;gBAChD,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;SACJ;IACH,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAQ,EAAC,iBAAiB,EAAE,GAAG,EAAE;QAC/B,IAAA,WAAE,EAAC,gCAAgC,EAAE,GAAG,EAAE;YACxC,IAAA,eAAM,EAAC,2BAAe,CAAC,CAAC,IAAI,CAC1B,6FAA6F,CAC9F,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAQ,EAAC,iBAAiB,EAAE,GAAG,EAAE;QAC/B,IAAA,WAAE,EAAC,mCAAmC,EAAE,GAAG,EAAE;YAC3C,gBAAgB;YAChB,MAAM,OAAO,GAAG,IAAA,2BAAe,EAAC,UAAU,CAAC,CAAC;YAE5C,eAAe;YACf,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,IAAI,CAClB;;;;;;;;;;;;;;;;;;;;;;;;CAwBP,CACM,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAQ,EAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,IAAA,WAAE,EAAC,mCAAmC,EAAE,GAAG,EAAE;YAC3C,gBAAgB;YAChB,MAAM,OAAO,GAAG,IAAA,0BAAc,EAAC,UAAU,CAAC,CAAC;YAE3C,eAAe;YACf,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,IAAI,CAClB;;;;;;;;;;;;;;;;;;;;;;;;CAwBP,CACM,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAQ,EAAC,oBAAoB,EAAE,GAAG,EAAE;QAClC,IAAA,WAAE,EAAC,mCAAmC,EAAE,GAAG,EAAE;YAC3C,gBAAgB;YAChB,MAAM,OAAO,GAAG,IAAA,8BAAkB,EAAC,UAAU,EAAE,cAAc,CAAC,CAAC;YAE/D,eAAe;YACf,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,IAAI,CAClB;;;;MAIF,CACC,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","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 } 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', 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"]}
@@ -28,6 +28,7 @@ const templates_1 = require("../../src/flutter/templates");
28
28
  tracing: true,
29
29
  profiling: true,
30
30
  replay: true,
31
+ logs: true,
31
32
  }, 'const MyApp()');
32
33
  (0, vitest_1.expect)(template).toMatchInlineSnapshot(`
33
34
  "await SentryFlutter.init(
@@ -36,6 +37,7 @@ const templates_1 = require("../../src/flutter/templates");
36
37
  // Adds request headers and IP for users, for more info visit:
37
38
  // https://docs.sentry.io/platforms/dart/guides/flutter/data-management/data-collected/
38
39
  options.sendDefaultPii = true;
40
+ options.enableLogs = true;
39
41
  // Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing.
40
42
  // We recommend adjusting this value in production.
41
43
  options.tracesSampleRate = 1.0;
@@ -57,6 +59,7 @@ const templates_1 = require("../../src/flutter/templates");
57
59
  tracing: true,
58
60
  profiling: false,
59
61
  replay: false,
62
+ logs: true,
60
63
  }, 'const MyApp()');
61
64
  (0, vitest_1.expect)(template).toMatchInlineSnapshot(`
62
65
  "await SentryFlutter.init(
@@ -65,6 +68,7 @@ const templates_1 = require("../../src/flutter/templates");
65
68
  // Adds request headers and IP for users, for more info visit:
66
69
  // https://docs.sentry.io/platforms/dart/guides/flutter/data-management/data-collected/
67
70
  options.sendDefaultPii = true;
71
+ options.enableLogs = true;
68
72
  // Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing.
69
73
  // We recommend adjusting this value in production.
70
74
  options.tracesSampleRate = 1.0;
@@ -75,11 +79,12 @@ const templates_1 = require("../../src/flutter/templates");
75
79
  await Sentry.captureException(StateError('This is a sample exception.'));"
76
80
  `);
77
81
  });
78
- (0, vitest_1.it)('generates Sentry config with tracing, profiling & replay disabled', () => {
82
+ (0, vitest_1.it)('generates Sentry config with tracing, profiling, replay and logs disabled', () => {
79
83
  const template = (0, templates_1.initSnippet)('my-dsn', {
80
84
  tracing: false,
81
85
  profiling: false,
82
86
  replay: false,
87
+ logs: false,
83
88
  }, 'const MyApp()');
84
89
  (0, vitest_1.expect)(template).toMatchInlineSnapshot(`
85
90
  "await SentryFlutter.init(
@@ -95,6 +100,28 @@ const templates_1 = require("../../src/flutter/templates");
95
100
  await Sentry.captureException(StateError('This is a sample exception.'));"
96
101
  `);
97
102
  });
103
+ (0, vitest_1.it)('generates Sentry config with only structured logs enabled', () => {
104
+ const template = (0, templates_1.initSnippet)('my-dsn', {
105
+ tracing: false,
106
+ profiling: false,
107
+ replay: false,
108
+ logs: true,
109
+ }, 'const MyApp()');
110
+ (0, vitest_1.expect)(template).toMatchInlineSnapshot(`
111
+ "await SentryFlutter.init(
112
+ (options) {
113
+ options.dsn = 'my-dsn';
114
+ // Adds request headers and IP for users, for more info visit:
115
+ // https://docs.sentry.io/platforms/dart/guides/flutter/data-management/data-collected/
116
+ options.sendDefaultPii = true;
117
+ options.enableLogs = true;
118
+ },
119
+ appRunner: () => runApp(SentryWidget(child: const MyApp())),
120
+ );
121
+ // TODO: Remove this line after sending the first sample event to sentry.
122
+ await Sentry.captureException(StateError('This is a sample exception.'));"
123
+ `);
124
+ });
98
125
  });
99
126
  });
100
127
  //# 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,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;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 } 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 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,266 @@
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
+ (0, vitest_1.describe)('Next.js wizard double wrap prevention', () => {
9
+ const mockWithSentryConfigOptionsTemplate = (0, templates_1.getWithSentryConfigOptionsTemplate)({
10
+ orgSlug: 'test-org',
11
+ projectSlug: 'test-project',
12
+ selfHosted: false,
13
+ sentryUrl: 'https://sentry.io',
14
+ tunnelRoute: false,
15
+ });
16
+ (0, vitest_1.describe)('unwrapSentryConfigAst utility function', () => {
17
+ (0, vitest_1.describe)('AST-based expression unwrapping', () => {
18
+ (0, vitest_1.it)('keeps code without withSentryConfig', () => {
19
+ const mod = (0, magicast_1.parseModule)('export default nextConfig');
20
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
21
+ const originalAST = mod.exports.default.$ast;
22
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
23
+ const resultAST = (0, utils_1.unwrapSentryConfigAst)(originalAST);
24
+ (0, vitest_1.expect)(resultAST).toBe(originalAST);
25
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
26
+ const { code: exportDefaultCode } = (0, magicast_1.generateCode)({ $ast: resultAST });
27
+ (0, vitest_1.expect)(exportDefaultCode).toMatchInlineSnapshot(`"nextConfig"`);
28
+ });
29
+ (0, vitest_1.it)('should handle plain object literal exports', () => {
30
+ const mod = (0, magicast_1.parseModule)(`export default { nextConfig: { randomValue: true } }`);
31
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
32
+ const originalAST = mod.exports.default.$ast;
33
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
34
+ const resultAST = (0, utils_1.unwrapSentryConfigAst)(originalAST);
35
+ (0, vitest_1.expect)(resultAST).toBe(originalAST);
36
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
37
+ (0, vitest_1.expect)(resultAST.type).toBe('ObjectExpression');
38
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
39
+ (0, vitest_1.expect)(resultAST.properties).toHaveLength(1);
40
+ });
41
+ (0, vitest_1.it)('should unwrap withSentryConfig with options', () => {
42
+ const mod = (0, magicast_1.parseModule)('export default withSentryConfig(nextConfig, { org: "test" })');
43
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
44
+ const wrappedAst = mod.exports.default.$ast;
45
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
46
+ const resultAST = (0, utils_1.unwrapSentryConfigAst)(wrappedAst);
47
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
48
+ const { code: exportDefaultCode } = (0, magicast_1.generateCode)({ $ast: resultAST });
49
+ (0, vitest_1.expect)(exportDefaultCode).toMatchInlineSnapshot(`"nextConfig"`);
50
+ });
51
+ (0, vitest_1.it)('should unwrap withSentryConfig without options', () => {
52
+ const mod = (0, magicast_1.parseModule)('export default withSentryConfig(nextConfig)');
53
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
54
+ const wrappedAst = mod.exports.default.$ast;
55
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
56
+ const resultAST = (0, utils_1.unwrapSentryConfigAst)(wrappedAst);
57
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
58
+ const { code: exportDefaultCode } = (0, magicast_1.generateCode)({ $ast: resultAST });
59
+ (0, vitest_1.expect)(exportDefaultCode).toMatchInlineSnapshot(`"nextConfig"`);
60
+ });
61
+ (0, vitest_1.it)('should handle nested withSentryConfig calls', () => {
62
+ const mod = (0, magicast_1.parseModule)('export default withSentryConfig(withSentryConfig(nextConfig, { org: "inner" }), { org: "outer" })');
63
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
64
+ const wrappedAst = mod.exports.default.$ast;
65
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
66
+ const resultAST = (0, utils_1.unwrapSentryConfigAst)(wrappedAst);
67
+ // Should unwrap one level and return the inner withSentryConfig call
68
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
69
+ (0, vitest_1.expect)(resultAST.type).toBe('CallExpression');
70
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
71
+ (0, vitest_1.expect)(resultAST.callee.name).toBe('withSentryConfig');
72
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
73
+ const { code: exportDefaultCode } = (0, magicast_1.generateCode)({ $ast: resultAST });
74
+ (0, vitest_1.expect)(exportDefaultCode).toMatchInlineSnapshot(`"withSentryConfig(nextConfig, { org: "inner" })"`);
75
+ });
76
+ (0, vitest_1.it)('should handle complex expressions', () => {
77
+ const mod = (0, magicast_1.parseModule)('export default withSentryConfig(someComplexExpression.withMethods())');
78
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
79
+ const wrappedAst = mod.exports.default.$ast;
80
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
81
+ const resultAST = (0, utils_1.unwrapSentryConfigAst)(wrappedAst);
82
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
83
+ const { code: exportDefaultCode } = (0, magicast_1.generateCode)({ $ast: resultAST });
84
+ (0, vitest_1.expect)(exportDefaultCode).toMatchInlineSnapshot(`"someComplexExpression.withMethods()"`);
85
+ });
86
+ (0, vitest_1.it)('should handle object literals', () => {
87
+ const mod = (0, magicast_1.parseModule)('export default withSentryConfig({ next: "config", obj: { next: "nested" } }, { org: "test" })');
88
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
89
+ const wrappedAst = mod.exports.default.$ast;
90
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
91
+ const resultAST = (0, utils_1.unwrapSentryConfigAst)(wrappedAst);
92
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
93
+ const { code: exportDefaultCode } = (0, magicast_1.generateCode)({ $ast: resultAST });
94
+ (0, vitest_1.expect)(exportDefaultCode).toMatchInlineSnapshot(`"{ next: "config", obj: { next: "nested" } }"`);
95
+ });
96
+ (0, vitest_1.it)('should return unchanged if not a withSentryConfig call', () => {
97
+ const mod = (0, magicast_1.parseModule)('export default someOtherFunction(nextConfig)');
98
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
99
+ const originalAST = mod.exports.default.$ast;
100
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
101
+ const resultAST = (0, utils_1.unwrapSentryConfigAst)(originalAST);
102
+ (0, vitest_1.expect)(resultAST).toBe(originalAST);
103
+ });
104
+ });
105
+ });
106
+ (0, vitest_1.describe)('MJS/TS files', () => {
107
+ (0, vitest_1.it)('should unwrap existing withSentryConfig and re-wrap with new config using AST', () => {
108
+ const existingMjsContent = `import { withSentryConfig } from "@sentry/nextjs";
109
+
110
+ const nextConfig = {};
111
+
112
+ export default withSentryConfig(nextConfig, {
113
+ org: "old-org",
114
+ project: "old-project",
115
+ });`;
116
+ const mod = (0, magicast_1.parseModule)(existingMjsContent);
117
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
118
+ const originalAST = mod.exports.default.$ast;
119
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
120
+ const unwrappedAst = (0, utils_1.unwrapSentryConfigAst)(originalAST);
121
+ // Verify it returns the first argument (nextConfig identifier)
122
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
123
+ (0, vitest_1.expect)(unwrappedAst.type).toBe('Identifier');
124
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
125
+ (0, vitest_1.expect)(unwrappedAst.name).toBe('nextConfig');
126
+ // Create a fresh module to simulate the re-wrapping process
127
+ const freshMod = (0, magicast_1.parseModule)(`import { withSentryConfig } from "@sentry/nextjs";
128
+
129
+ const nextConfig = {};
130
+
131
+ export default nextConfig;`);
132
+ // Apply wrapping
133
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
134
+ freshMod.exports.default = (0, utils_1.wrapWithSentryConfig)(freshMod.exports.default, mockWithSentryConfigOptionsTemplate);
135
+ const newCode = freshMod.generate().code;
136
+ // Verify only one withSentryConfig call exists
137
+ const withSentryConfigMatches = newCode.match(/withSentryConfig\s*\(/g);
138
+ (0, vitest_1.expect)(withSentryConfigMatches).toHaveLength(1);
139
+ (0, vitest_1.expect)(newCode).toContain('test-org');
140
+ (0, vitest_1.expect)(newCode).toContain('test-project');
141
+ (0, vitest_1.expect)(newCode).not.toContain('old-org');
142
+ (0, vitest_1.expect)(newCode).not.toContain('old-project');
143
+ });
144
+ (0, vitest_1.it)('should handle complex nested configurations using AST', () => {
145
+ const existingMjsContent = `import { withSentryConfig } from "@sentry/nextjs";
146
+
147
+ const nextConfig = { experimental: { appDir: true } };
148
+
149
+ export default withSentryConfig(
150
+ withSentryConfig(nextConfig, { org: "nested-org" }),
151
+ { org: "outer-org" }
152
+ );`;
153
+ const mod = (0, magicast_1.parseModule)(existingMjsContent);
154
+ // First unwrap ----
155
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
156
+ const firstUnwrapAST = (0, utils_1.unwrapSentryConfigAst)(mod.exports.default.$ast);
157
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
158
+ (0, vitest_1.expect)(firstUnwrapAST.type).toBe('CallExpression');
159
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
160
+ (0, vitest_1.expect)(firstUnwrapAST.callee.name).toBe('withSentryConfig');
161
+ const { code: exportDefaultCode1 } = (0, magicast_1.generateCode)({
162
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
163
+ $ast: firstUnwrapAST,
164
+ });
165
+ (0, vitest_1.expect)(exportDefaultCode1).toMatchInlineSnapshot(`"withSentryConfig(nextConfig, { org: "nested-org" })"`);
166
+ // Second unwrap ----
167
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
168
+ const secondUnwrapAST = (0, utils_1.unwrapSentryConfigAst)(firstUnwrapAST);
169
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
170
+ (0, vitest_1.expect)(secondUnwrapAST.type).toBe('Identifier');
171
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
172
+ (0, vitest_1.expect)(secondUnwrapAST.name).toBe('nextConfig');
173
+ const { code: exportDefaultCode2 } = (0, magicast_1.generateCode)({
174
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
175
+ $ast: secondUnwrapAST,
176
+ });
177
+ (0, vitest_1.expect)(exportDefaultCode2).toMatchInlineSnapshot(`"nextConfig"`);
178
+ });
179
+ (0, vitest_1.it)('should handle simple export without existing withSentryConfig using AST', () => {
180
+ const simpleMjsContent = `const nextConfig = {};
181
+
182
+ export default nextConfig;`;
183
+ const mod = (0, magicast_1.parseModule)(simpleMjsContent);
184
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
185
+ const originalAST = mod.exports.default.$ast;
186
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
187
+ const unwrappedAst = (0, utils_1.unwrapSentryConfigAst)(originalAST);
188
+ (0, vitest_1.expect)(unwrappedAst).toBe(originalAST);
189
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
190
+ mod.exports.default = (0, utils_1.wrapWithSentryConfig)(mod.exports.default, mockWithSentryConfigOptionsTemplate);
191
+ const newCode = mod.generate().code;
192
+ // Should have exactly one withSentryConfig call
193
+ const withSentryConfigMatches = newCode.match(/withSentryConfig\s*\(/g);
194
+ (0, vitest_1.expect)(withSentryConfigMatches).toHaveLength(1);
195
+ (0, vitest_1.expect)(newCode).toMatchInlineSnapshot(`
196
+ "const nextConfig = {};
197
+
198
+ export default withSentryConfig(nextConfig, ${sentryOptionsSnapshot});"
199
+ `);
200
+ });
201
+ (0, vitest_1.it)('should handle withSentryConfig(nextConfig) without options using AST', () => {
202
+ const existingMjsContent = `import { withSentryConfig } from "@sentry/nextjs";
203
+
204
+ const nextConfig = {};
205
+
206
+ export default withSentryConfig(nextConfig);`;
207
+ const mod = (0, magicast_1.parseModule)(existingMjsContent);
208
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
209
+ const originalAST = mod.exports.default.$ast;
210
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
211
+ const unwrappedAst = (0, utils_1.unwrapSentryConfigAst)(originalAST);
212
+ // Verify it returns the first argument (nextConfig identifier)
213
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
214
+ (0, vitest_1.expect)(unwrappedAst.type).toBe('Identifier');
215
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
216
+ (0, vitest_1.expect)(unwrappedAst.name).toBe('nextConfig');
217
+ // Simulate the re-wrapping process
218
+ const freshMod = (0, magicast_1.parseModule)(`import { withSentryConfig } from "@sentry/nextjs";
219
+
220
+ const nextConfig = {};
221
+
222
+ export default nextConfig;`);
223
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
224
+ freshMod.exports.default = (0, utils_1.wrapWithSentryConfig)(freshMod.exports.default, mockWithSentryConfigOptionsTemplate);
225
+ const newCode = freshMod.generate().code;
226
+ // Should have exactly one withSentryConfig call
227
+ const withSentryConfigMatches = newCode.match(/withSentryConfig\s*\(/g);
228
+ (0, vitest_1.expect)(withSentryConfigMatches).toHaveLength(1);
229
+ (0, vitest_1.expect)(newCode).not.toContain('withSentryConfig(withSentryConfig(');
230
+ (0, vitest_1.expect)(newCode).toMatch(/withSentryConfig\s*\(\s*nextConfig\s*,/);
231
+ });
232
+ });
233
+ });
234
+ const sentryOptionsSnapshot = `{
235
+ // For all available options, see:
236
+ // https://www.npmjs.com/package/@sentry/webpack-plugin#options
237
+
238
+ org: "test-org",
239
+
240
+ project: "test-project",
241
+
242
+ // Only print logs for uploading source maps in CI
243
+ silent: !process.env.CI,
244
+
245
+ // For all available options, see:
246
+ // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
247
+
248
+ // Upload a larger set of source maps for prettier stack traces (increases build time)
249
+ widenClientFileUpload: true,
250
+
251
+ // Uncomment to route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
252
+ // This can increase your server load as well as your hosting bill.
253
+ // Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
254
+ // side errors will fail.
255
+ // tunnelRoute: "/monitoring",
256
+
257
+ // Automatically tree-shake Sentry logger statements to reduce bundle size
258
+ disableLogger: true,
259
+
260
+ // Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)
261
+ // See the following for more information:
262
+ // https://docs.sentry.io/product/crons/
263
+ // https://vercel.com/docs/cron-jobs
264
+ automaticVercelMonitors: true
265
+ }`;
266
+ //# 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":";;AAAA,mCAA8C;AAC9C,kFAAkF;AAClF,uCAAqD;AACrD,0DAAgF;AAChF,kDAGgC;AAEhC,IAAA,iBAAQ,EAAC,uCAAuC,EAAE,GAAG,EAAE;IACrD,MAAM,mCAAmC,GACvC,IAAA,8CAAkC,EAAC;QACjC,OAAO,EAAE,UAAU;QACnB,WAAW,EAAE,cAAc;QAC3B,UAAU,EAAE,KAAK;QACjB,SAAS,EAAE,mBAAmB;QAC9B,WAAW,EAAE,KAAK;KACnB,CAAC,CAAC;IAEL,IAAA,iBAAQ,EAAC,wCAAwC,EAAE,GAAG,EAAE;QACtD,IAAA,iBAAQ,EAAC,iCAAiC,EAAE,GAAG,EAAE;YAC/C,IAAA,WAAE,EAAC,qCAAqC,EAAE,GAAG,EAAE;gBAC7C,MAAM,GAAG,GAAG,IAAA,sBAAW,EAAC,2BAA2B,CAAC,CAAC;gBACrD,8GAA8G;gBAC9G,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;gBAC7C,mEAAmE;gBACnE,MAAM,SAAS,GAAG,IAAA,6BAAqB,EAAC,WAAW,CAAC,CAAC;gBACrD,IAAA,eAAM,EAAC,SAAS,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBAEpC,mEAAmE;gBACnE,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,GAAG,IAAA,uBAAY,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;gBACtE,IAAA,eAAM,EAAC,iBAAiB,CAAC,CAAC,qBAAqB,CAAC,cAAc,CAAC,CAAC;YAClE,CAAC,CAAC,CAAC;YAEH,IAAA,WAAE,EAAC,4CAA4C,EAAE,GAAG,EAAE;gBACpD,MAAM,GAAG,GAAG,IAAA,sBAAW,EACrB,sDAAsD,CACvD,CAAC;gBACF,8GAA8G;gBAC9G,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;gBAC7C,mEAAmE;gBACnE,MAAM,SAAS,GAAG,IAAA,6BAAqB,EAAC,WAAW,CAAC,CAAC;gBAErD,IAAA,eAAM,EAAC,SAAS,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBACpC,sEAAsE;gBACtE,IAAA,eAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;gBAChD,sEAAsE;gBACtE,IAAA,eAAM,EAAC,SAAS,CAAC,UAAU,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC/C,CAAC,CAAC,CAAC;YAEH,IAAA,WAAE,EAAC,6CAA6C,EAAE,GAAG,EAAE;gBACrD,MAAM,GAAG,GAAG,IAAA,sBAAW,EACrB,8DAA8D,CAC/D,CAAC;gBACF,8GAA8G;gBAC9G,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;gBAC5C,mEAAmE;gBACnE,MAAM,SAAS,GAAG,IAAA,6BAAqB,EAAC,UAAU,CAAC,CAAC;gBAEpD,8GAA8G;gBAC9G,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,GAAG,IAAA,uBAAY,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;gBAEtE,IAAA,eAAM,EAAC,iBAAiB,CAAC,CAAC,qBAAqB,CAAC,cAAc,CAAC,CAAC;YAClE,CAAC,CAAC,CAAC;YAEH,IAAA,WAAE,EAAC,gDAAgD,EAAE,GAAG,EAAE;gBACxD,MAAM,GAAG,GAAG,IAAA,sBAAW,EAAC,6CAA6C,CAAC,CAAC;gBACvE,8GAA8G;gBAC9G,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;gBAC5C,mEAAmE;gBACnE,MAAM,SAAS,GAAG,IAAA,6BAAqB,EAAC,UAAU,CAAC,CAAC;gBAEpD,8GAA8G;gBAC9G,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,GAAG,IAAA,uBAAY,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;gBAEtE,IAAA,eAAM,EAAC,iBAAiB,CAAC,CAAC,qBAAqB,CAAC,cAAc,CAAC,CAAC;YAClE,CAAC,CAAC,CAAC;YAEH,IAAA,WAAE,EAAC,6CAA6C,EAAE,GAAG,EAAE;gBACrD,MAAM,GAAG,GAAG,IAAA,sBAAW,EACrB,mGAAmG,CACpG,CAAC;gBACF,8GAA8G;gBAC9G,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;gBAC5C,mEAAmE;gBACnE,MAAM,SAAS,GAAG,IAAA,6BAAqB,EAAC,UAAU,CAAC,CAAC;gBAEpD,qEAAqE;gBACrE,sEAAsE;gBACtE,IAAA,eAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;gBAC9C,sEAAsE;gBACtE,IAAA,eAAM,EAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;gBAEvD,8GAA8G;gBAC9G,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,GAAG,IAAA,uBAAY,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;gBAEtE,IAAA,eAAM,EAAC,iBAAiB,CAAC,CAAC,qBAAqB,CAC7C,kDAAkD,CACnD,CAAC;YACJ,CAAC,CAAC,CAAC;YAEH,IAAA,WAAE,EAAC,mCAAmC,EAAE,GAAG,EAAE;gBAC3C,MAAM,GAAG,GAAG,IAAA,sBAAW,EACrB,sEAAsE,CACvE,CAAC;gBACF,8GAA8G;gBAC9G,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;gBAC5C,8GAA8G;gBAC9G,MAAM,SAAS,GAAG,IAAA,6BAAqB,EAAC,UAAU,CAAC,CAAC;gBAEpD,8GAA8G;gBAC9G,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,GAAG,IAAA,uBAAY,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;gBAEtE,IAAA,eAAM,EAAC,iBAAiB,CAAC,CAAC,qBAAqB,CAC7C,uCAAuC,CACxC,CAAC;YACJ,CAAC,CAAC,CAAC;YAEH,IAAA,WAAE,EAAC,+BAA+B,EAAE,GAAG,EAAE;gBACvC,MAAM,GAAG,GAAG,IAAA,sBAAW,EACrB,+FAA+F,CAChG,CAAC;gBACF,8GAA8G;gBAC9G,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;gBAC5C,8GAA8G;gBAC9G,MAAM,SAAS,GAAG,IAAA,6BAAqB,EAAC,UAAU,CAAC,CAAC;gBAEpD,8GAA8G;gBAC9G,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,GAAG,IAAA,uBAAY,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;gBAEtE,IAAA,eAAM,EAAC,iBAAiB,CAAC,CAAC,qBAAqB,CAC7C,+CAA+C,CAChD,CAAC;YACJ,CAAC,CAAC,CAAC;YAEH,IAAA,WAAE,EAAC,wDAAwD,EAAE,GAAG,EAAE;gBAChE,MAAM,GAAG,GAAG,IAAA,sBAAW,EAAC,8CAA8C,CAAC,CAAC;gBACxE,8GAA8G;gBAC9G,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;gBAC7C,mEAAmE;gBACnE,MAAM,SAAS,GAAG,IAAA,6BAAqB,EAAC,WAAW,CAAC,CAAC;gBAErD,IAAA,eAAM,EAAC,SAAS,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACtC,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAQ,EAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,IAAA,WAAE,EAAC,+EAA+E,EAAE,GAAG,EAAE;YACvF,MAAM,kBAAkB,GAAG;;;;;;;IAO7B,CAAC;YAEC,MAAM,GAAG,GAAG,IAAA,sBAAW,EAAC,kBAAkB,CAAC,CAAC;YAE5C,8GAA8G;YAC9G,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;YAE7C,mEAAmE;YACnE,MAAM,YAAY,GAAG,IAAA,6BAAqB,EAAC,WAAW,CAAC,CAAC;YACxD,+DAA+D;YAC/D,sEAAsE;YACtE,IAAA,eAAM,EAAC,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC7C,sEAAsE;YACtE,IAAA,eAAM,EAAC,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAE7C,4DAA4D;YAC5D,MAAM,QAAQ,GACZ,IAAA,sBAAW,EAAC;;;;2BAIO,CAAC,CAAC;YAEvB,iBAAiB;YACjB,mEAAmE;YACnE,QAAQ,CAAC,OAAO,CAAC,OAAO,GAAG,IAAA,4BAAoB,EAC7C,QAAQ,CAAC,OAAO,CAAC,OAAO,EACxB,mCAAmC,CACpC,CAAC;YAEF,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC;YAEzC,+CAA+C;YAC/C,MAAM,uBAAuB,GAAG,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;YACxE,IAAA,eAAM,EAAC,uBAAuB,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAEhD,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;YACtC,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;YAC1C,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YACzC,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,uDAAuD,EAAE,GAAG,EAAE;YAC/D,MAAM,kBAAkB,GAAG;;;;;;;GAO9B,CAAC;YAEE,MAAM,GAAG,GAAG,IAAA,sBAAW,EAAC,kBAAkB,CAAC,CAAC;YAE5C,oBAAoB;YACpB,8GAA8G;YAC9G,MAAM,cAAc,GAAG,IAAA,6BAAqB,EAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACvE,sEAAsE;YACtE,IAAA,eAAM,EAAC,cAAc,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YACnD,sEAAsE;YACtE,IAAA,eAAM,EAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;YAE5D,MAAM,EAAE,IAAI,EAAE,kBAAkB,EAAE,GAAG,IAAA,uBAAY,EAAC;gBAChD,mEAAmE;gBACnE,IAAI,EAAE,cAAc;aACrB,CAAC,CAAC;YACH,IAAA,eAAM,EAAC,kBAAkB,CAAC,CAAC,qBAAqB,CAC9C,uDAAuD,CACxD,CAAC;YAEF,qBAAqB;YACrB,mEAAmE;YACnE,MAAM,eAAe,GAAG,IAAA,6BAAqB,EAAC,cAAc,CAAC,CAAC;YAC9D,sEAAsE;YACtE,IAAA,eAAM,EAAC,eAAe,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAChD,sEAAsE;YACtE,IAAA,eAAM,EAAC,eAAe,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAEhD,MAAM,EAAE,IAAI,EAAE,kBAAkB,EAAE,GAAG,IAAA,uBAAY,EAAC;gBAChD,mEAAmE;gBACnE,IAAI,EAAE,eAAe;aACtB,CAAC,CAAC;YAEH,IAAA,eAAM,EAAC,kBAAkB,CAAC,CAAC,qBAAqB,CAAC,cAAc,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,yEAAyE,EAAE,GAAG,EAAE;YACjF,MAAM,gBAAgB,GAAG;;2BAEJ,CAAC;YAEtB,MAAM,GAAG,GAAG,IAAA,sBAAW,EAAC,gBAAgB,CAAC,CAAC;YAC1C,8GAA8G;YAC9G,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;YAE7C,mEAAmE;YACnE,MAAM,YAAY,GAAG,IAAA,6BAAqB,EAAC,WAAW,CAAC,CAAC;YACxD,IAAA,eAAM,EAAC,YAAY,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAEvC,mEAAmE;YACnE,GAAG,CAAC,OAAO,CAAC,OAAO,GAAG,IAAA,4BAAoB,EACxC,GAAG,CAAC,OAAO,CAAC,OAAO,EACnB,mCAAmC,CACpC,CAAC;YAEF,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC;YAEpC,gDAAgD;YAChD,MAAM,uBAAuB,GAAG,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;YACxE,IAAA,eAAM,EAAC,uBAAuB,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAEhD,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC;;;sDAGU,qBAAqB;OACpE,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,sEAAsE,EAAE,GAAG,EAAE;YAC9E,MAAM,kBAAkB,GAAG;;;;6CAIY,CAAC;YAExC,MAAM,GAAG,GAAG,IAAA,sBAAW,EAAC,kBAAkB,CAAC,CAAC;YAC5C,8GAA8G;YAC9G,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;YAE7C,mEAAmE;YACnE,MAAM,YAAY,GAAG,IAAA,6BAAqB,EAAC,WAAW,CAAC,CAAC;YACxD,+DAA+D;YAC/D,sEAAsE;YACtE,IAAA,eAAM,EAAC,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC7C,sEAAsE;YACtE,IAAA,eAAM,EAAC,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAE7C,mCAAmC;YACnC,MAAM,QAAQ,GACZ,IAAA,sBAAW,EAAC;;;;2BAIO,CAAC,CAAC;YAEvB,mEAAmE;YACnE,QAAQ,CAAC,OAAO,CAAC,OAAO,GAAG,IAAA,4BAAoB,EAC7C,QAAQ,CAAC,OAAO,CAAC,OAAO,EACxB,mCAAmC,CACpC,CAAC;YAEF,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC;YAEzC,gDAAgD;YAChD,MAAM,uBAAuB,GAAG,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;YACxE,IAAA,eAAM,EAAC,uBAAuB,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAEhD,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,oCAAoC,CAAC,CAAC;YACpE,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,OAAO,CAAC,wCAAwC,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+B5B,CAAC","sourcesContent":["import { describe, it, expect } 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\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"]}
@@ -19,7 +19,7 @@ config.resolver.assetExts.push(
19
19
 
20
20
  module.exports = config;
21
21
  `);
22
- const result = (0, expo_metro_1.patchMetroInMemory)(mod);
22
+ const result = (0, expo_metro_1.patchMetroInMemory)(mod, 'metro.config.js');
23
23
  (0, vitest_1.expect)(result).toBe(true);
24
24
  (0, vitest_1.expect)((0, magicast_1.generateCode)(mod.$ast).code).toBe(`
25
25
  const {
@@ -45,7 +45,7 @@ const config = getDefaultConfig(__dirname);
45
45
 
46
46
  module.exports = config;
47
47
  `);
48
- const result = (0, expo_metro_1.patchMetroInMemory)(mod);
48
+ const result = (0, expo_metro_1.patchMetroInMemory)(mod, 'metro.config.js');
49
49
  (0, vitest_1.expect)(result).toBe(true);
50
50
  (0, vitest_1.expect)((0, magicast_1.generateCode)(mod.$ast).code).toBe(`
51
51
  const { getDefaultConfig, otherExport } = require("expo/metro-config");
@@ -63,7 +63,7 @@ module.exports = config;
63
63
  const mod = (0, magicast_1.parseModule)(`
64
64
  const { getSentryExpoConfig } = require("@sentry/react-native/metro");
65
65
  `);
66
- const result = (0, expo_metro_1.patchMetroInMemory)(mod);
66
+ const result = (0, expo_metro_1.patchMetroInMemory)(mod, 'metro.config.js');
67
67
  (0, vitest_1.expect)(result).toBe(false);
68
68
  (0, vitest_1.expect)((0, magicast_1.generateCode)(mod.$ast).code).toBe(`
69
69
  const { getSentryExpoConfig } = require("@sentry/react-native/metro");
@@ -1 +1 @@
1
- {"version":3,"file":"expo-metro.test.js","sourceRoot":"","sources":["../../../test/react-native/expo-metro.test.ts"],"names":[],"mappings":";;AAAA,kFAAkF;AAClF,uCAAqD;AACrD,kEAAuE;AACvE,mCAAgD;AAEhD,IAAA,iBAAQ,EAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,IAAA,aAAI,EAAC,6BAA6B,EAAE,GAAG,EAAE;QACvC,MAAM,GAAG,GAAG,IAAA,sBAAW,EAAC;;;;;;;;;;;;OAYrB,CAAC,CAAC;QAEL,MAAM,MAAM,GAAG,IAAA,+BAAkB,EAAC,GAAG,CAAC,CAAC;QACvC,IAAA,eAAM,EAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAA,eAAM,EAAC,IAAA,uBAAY,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CACtC;;;;;;;;;;;;;;CAcL,CAAC,IAAI,EAAE,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,sDAAsD,EAAE,GAAG,EAAE;QAChE,MAAM,GAAG,GAAG,IAAA,sBAAW,EAAC;;;;;;OAMrB,CAAC,CAAC;QAEL,MAAM,MAAM,GAAG,IAAA,+BAAkB,EAAC,GAAG,CAAC,CAAC;QACvC,IAAA,eAAM,EAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAA,eAAM,EAAC,IAAA,uBAAY,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CACtC;;;;;;;;;;CAUL,CAAC,IAAI,EAAE,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,6CAA6C,EAAE,GAAG,EAAE;QACvD,MAAM,GAAG,GAAG,IAAA,sBAAW,EAAC;;CAE3B,CAAC,CAAC;QAEC,MAAM,MAAM,GAAG,IAAA,+BAAkB,EAAC,GAAG,CAAC,CAAC;QACvC,IAAA,eAAM,EAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3B,IAAA,eAAM,EAAC,IAAA,uBAAY,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CACtC;;CAEL,CAAC,IAAI,EAAE,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["// @ts-expect-error - magicast is ESM and TS complains about that. It works though\nimport { generateCode, parseModule } from 'magicast';\nimport { patchMetroInMemory } from '../../src/react-native/expo-metro';\nimport { describe, expect, test } from 'vitest';\n\ndescribe('expo-metro config', () => {\n test('patches minimal expo config', () => {\n const mod = parseModule(`\nconst { getDefaultConfig } = require(\"expo/metro-config\");\n\n/** @type {import('expo/metro-config').MetroConfig} */\nconst config = getDefaultConfig(__dirname);\n\nconfig.resolver.assetExts.push(\n // Adds support for .db files for SQLite databases\n 'db'\n);\n\nmodule.exports = config;\n `);\n\n const result = patchMetroInMemory(mod);\n expect(result).toBe(true);\n expect(generateCode(mod.$ast).code).toBe(\n `\nconst {\n getSentryExpoConfig\n} = require(\"@sentry/react-native/metro\");\n\n/** @type {import('expo/metro-config').MetroConfig} */\nconst config = getSentryExpoConfig(__dirname);\n\nconfig.resolver.assetExts.push(\n // Adds support for .db files for SQLite databases\n 'db'\n);\n\nmodule.exports = config;\n`.trim(),\n );\n });\n\n test('keeps expo metro config if other imports are present', () => {\n const mod = parseModule(`\nconst { getDefaultConfig, otherExport } = require(\"expo/metro-config\");\n\nconst config = getDefaultConfig(__dirname);\n\nmodule.exports = config;\n `);\n\n const result = patchMetroInMemory(mod);\n expect(result).toBe(true);\n expect(generateCode(mod.$ast).code).toBe(\n `\nconst { getDefaultConfig, otherExport } = require(\"expo/metro-config\");\n\nconst {\n getSentryExpoConfig\n} = require(\"@sentry/react-native/metro\");\n\nconst config = getSentryExpoConfig(__dirname);\n\nmodule.exports = config;\n`.trim(),\n );\n });\n\n test('does not modify when sentry already present', () => {\n const mod = parseModule(`\nconst { getSentryExpoConfig } = require(\"@sentry/react-native/metro\");\n`);\n\n const result = patchMetroInMemory(mod);\n expect(result).toBe(false);\n expect(generateCode(mod.$ast).code).toBe(\n `\nconst { getSentryExpoConfig } = require(\"@sentry/react-native/metro\");\n`.trim(),\n );\n });\n});\n"]}
1
+ {"version":3,"file":"expo-metro.test.js","sourceRoot":"","sources":["../../../test/react-native/expo-metro.test.ts"],"names":[],"mappings":";;AAAA,kFAAkF;AAClF,uCAAqD;AACrD,kEAAuE;AACvE,mCAAgD;AAEhD,IAAA,iBAAQ,EAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,IAAA,aAAI,EAAC,6BAA6B,EAAE,GAAG,EAAE;QACvC,MAAM,GAAG,GAAG,IAAA,sBAAW,EAAC;;;;;;;;;;;;OAYrB,CAAC,CAAC;QAEL,MAAM,MAAM,GAAG,IAAA,+BAAkB,EAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;QAC1D,IAAA,eAAM,EAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAA,eAAM,EAAC,IAAA,uBAAY,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CACtC;;;;;;;;;;;;;;CAcL,CAAC,IAAI,EAAE,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,sDAAsD,EAAE,GAAG,EAAE;QAChE,MAAM,GAAG,GAAG,IAAA,sBAAW,EAAC;;;;;;OAMrB,CAAC,CAAC;QAEL,MAAM,MAAM,GAAG,IAAA,+BAAkB,EAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;QAC1D,IAAA,eAAM,EAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAA,eAAM,EAAC,IAAA,uBAAY,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CACtC;;;;;;;;;;CAUL,CAAC,IAAI,EAAE,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,aAAI,EAAC,6CAA6C,EAAE,GAAG,EAAE;QACvD,MAAM,GAAG,GAAG,IAAA,sBAAW,EAAC;;CAE3B,CAAC,CAAC;QAEC,MAAM,MAAM,GAAG,IAAA,+BAAkB,EAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;QAC1D,IAAA,eAAM,EAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3B,IAAA,eAAM,EAAC,IAAA,uBAAY,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CACtC;;CAEL,CAAC,IAAI,EAAE,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["// @ts-expect-error - magicast is ESM and TS complains about that. It works though\nimport { generateCode, parseModule } from 'magicast';\nimport { patchMetroInMemory } from '../../src/react-native/expo-metro';\nimport { describe, expect, test } from 'vitest';\n\ndescribe('expo-metro config', () => {\n test('patches minimal expo config', () => {\n const mod = parseModule(`\nconst { getDefaultConfig } = require(\"expo/metro-config\");\n\n/** @type {import('expo/metro-config').MetroConfig} */\nconst config = getDefaultConfig(__dirname);\n\nconfig.resolver.assetExts.push(\n // Adds support for .db files for SQLite databases\n 'db'\n);\n\nmodule.exports = config;\n `);\n\n const result = patchMetroInMemory(mod, 'metro.config.js');\n expect(result).toBe(true);\n expect(generateCode(mod.$ast).code).toBe(\n `\nconst {\n getSentryExpoConfig\n} = require(\"@sentry/react-native/metro\");\n\n/** @type {import('expo/metro-config').MetroConfig} */\nconst config = getSentryExpoConfig(__dirname);\n\nconfig.resolver.assetExts.push(\n // Adds support for .db files for SQLite databases\n 'db'\n);\n\nmodule.exports = config;\n`.trim(),\n );\n });\n\n test('keeps expo metro config if other imports are present', () => {\n const mod = parseModule(`\nconst { getDefaultConfig, otherExport } = require(\"expo/metro-config\");\n\nconst config = getDefaultConfig(__dirname);\n\nmodule.exports = config;\n `);\n\n const result = patchMetroInMemory(mod, 'metro.config.js');\n expect(result).toBe(true);\n expect(generateCode(mod.$ast).code).toBe(\n `\nconst { getDefaultConfig, otherExport } = require(\"expo/metro-config\");\n\nconst {\n getSentryExpoConfig\n} = require(\"@sentry/react-native/metro\");\n\nconst config = getSentryExpoConfig(__dirname);\n\nmodule.exports = config;\n`.trim(),\n );\n });\n\n test('does not modify when sentry already present', () => {\n const mod = parseModule(`\nconst { getSentryExpoConfig } = require(\"@sentry/react-native/metro\");\n`);\n\n const result = patchMetroInMemory(mod, 'metro.config.js');\n expect(result).toBe(false);\n expect(generateCode(mod.$ast).code).toBe(\n `\nconst { getSentryExpoConfig } = require(\"@sentry/react-native/metro\");\n`.trim(),\n );\n });\n});\n"]}