@sentry/wizard 6.12.0 → 6.13.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 (184) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/bin.js +16 -1
  3. package/dist/bin.js.map +1 -1
  4. package/dist/e2e-tests/tests/angular-17.test.js +3 -4
  5. package/dist/e2e-tests/tests/angular-17.test.js.map +1 -1
  6. package/dist/e2e-tests/tests/angular-19.test.js +3 -4
  7. package/dist/e2e-tests/tests/angular-19.test.js.map +1 -1
  8. package/dist/e2e-tests/tests/flutter.test.js +60 -0
  9. package/dist/e2e-tests/tests/flutter.test.js.map +1 -1
  10. package/dist/e2e-tests/tests/help-message.test.js +8 -3
  11. package/dist/e2e-tests/tests/help-message.test.js.map +1 -1
  12. package/dist/e2e-tests/tests/nuxt-3.test.js +12 -6
  13. package/dist/e2e-tests/tests/nuxt-3.test.js.map +1 -1
  14. package/dist/e2e-tests/tests/nuxt-4.test.js +12 -6
  15. package/dist/e2e-tests/tests/nuxt-4.test.js.map +1 -1
  16. package/dist/e2e-tests/tests/pnpm-workspace.test.js +6 -3
  17. package/dist/e2e-tests/tests/pnpm-workspace.test.js.map +1 -1
  18. package/dist/e2e-tests/tests/react-router-instrumentation-api.test.js +4 -4
  19. package/dist/e2e-tests/tests/react-router-instrumentation-api.test.js.map +1 -1
  20. package/dist/e2e-tests/tests/react-router.test.js +3 -6
  21. package/dist/e2e-tests/tests/react-router.test.js.map +1 -1
  22. package/dist/e2e-tests/tests/remix.test.js +2 -4
  23. package/dist/e2e-tests/tests/remix.test.js.map +1 -1
  24. package/dist/e2e-tests/tests/sveltekit-hooks.test.js +24 -8
  25. package/dist/e2e-tests/tests/sveltekit-hooks.test.js.map +1 -1
  26. package/dist/e2e-tests/tests/sveltekit-tracing.test.js +6 -3
  27. package/dist/e2e-tests/tests/sveltekit-tracing.test.js.map +1 -1
  28. package/dist/lib/Constants.d.ts +1 -0
  29. package/dist/lib/Constants.js +5 -0
  30. package/dist/lib/Constants.js.map +1 -1
  31. package/dist/lib/Steps/Integrations/Electron.js +2 -2
  32. package/dist/lib/Steps/Integrations/Electron.js.map +1 -1
  33. package/dist/src/android/android-wizard.js +3 -0
  34. package/dist/src/android/android-wizard.js.map +1 -1
  35. package/dist/src/angular/codemods/main.d.ts +1 -1
  36. package/dist/src/angular/codemods/main.js +0 -1
  37. package/dist/src/angular/codemods/main.js.map +1 -1
  38. package/dist/src/apple/apple-wizard.js +2 -3
  39. package/dist/src/apple/apple-wizard.js.map +1 -1
  40. package/dist/src/apple/check-installed-cli.d.ts +1 -1
  41. package/dist/src/apple/check-installed-cli.js +13 -7
  42. package/dist/src/apple/check-installed-cli.js.map +1 -1
  43. package/dist/src/apple/configure-xcode-project.js +8 -1
  44. package/dist/src/apple/configure-xcode-project.js.map +1 -1
  45. package/dist/src/apple/lookup-xcode-project.d.ts +8 -5
  46. package/dist/src/apple/lookup-xcode-project.js +22 -17
  47. package/dist/src/apple/lookup-xcode-project.js.map +1 -1
  48. package/dist/src/apple/options.d.ts +5 -0
  49. package/dist/src/apple/options.js.map +1 -1
  50. package/dist/src/apple/sentry-swift-package.d.ts +4 -0
  51. package/dist/src/apple/sentry-swift-package.js +17 -0
  52. package/dist/src/apple/sentry-swift-package.js.map +1 -0
  53. package/dist/src/apple/snapshots/apple-snapshots-wizard.d.ts +2 -0
  54. package/dist/src/apple/snapshots/apple-snapshots-wizard.js +251 -0
  55. package/dist/src/apple/snapshots/apple-snapshots-wizard.js.map +1 -0
  56. package/dist/src/apple/snapshots/configure-snapshotpreviews-xcode-project.d.ts +13 -0
  57. package/dist/src/apple/snapshots/configure-snapshotpreviews-xcode-project.js +48 -0
  58. package/dist/src/apple/snapshots/configure-snapshotpreviews-xcode-project.js.map +1 -0
  59. package/dist/src/apple/snapshots/snapshot-test-file.d.ts +18 -0
  60. package/dist/src/apple/snapshots/snapshot-test-file.js +122 -0
  61. package/dist/src/apple/snapshots/snapshot-test-file.js.map +1 -0
  62. package/dist/src/apple/snapshots/snapshot-verification-scheme.d.ts +6 -0
  63. package/dist/src/apple/snapshots/snapshot-verification-scheme.js +147 -0
  64. package/dist/src/apple/snapshots/snapshot-verification-scheme.js.map +1 -0
  65. package/dist/src/apple/snapshots/snapshotpreviews-package.d.ts +4 -0
  66. package/dist/src/apple/snapshots/snapshotpreviews-package.js +8 -0
  67. package/dist/src/apple/snapshots/snapshotpreviews-package.js.map +1 -0
  68. package/dist/src/apple/snapshots/snapshots-cli-preflight.d.ts +23 -0
  69. package/dist/src/apple/snapshots/snapshots-cli-preflight.js +136 -0
  70. package/dist/src/apple/snapshots/snapshots-cli-preflight.js.map +1 -0
  71. package/dist/src/apple/xcode-manager.d.ts +59 -1
  72. package/dist/src/apple/xcode-manager.js +507 -106
  73. package/dist/src/apple/xcode-manager.js.map +1 -1
  74. package/dist/src/flutter/flutter-wizard.js +3 -0
  75. package/dist/src/flutter/flutter-wizard.js.map +1 -1
  76. package/dist/src/nextjs/templates.js +12 -6
  77. package/dist/src/nextjs/templates.js.map +1 -1
  78. package/dist/src/nuxt/templates.js +12 -6
  79. package/dist/src/nuxt/templates.js.map +1 -1
  80. package/dist/src/react-router/codemods/client.entry.d.ts +1 -1
  81. package/dist/src/react-router/codemods/client.entry.js +93 -52
  82. package/dist/src/react-router/codemods/client.entry.js.map +1 -1
  83. package/dist/src/react-router/codemods/server-entry.js +8 -7
  84. package/dist/src/react-router/codemods/server-entry.js.map +1 -1
  85. package/dist/src/react-router/react-router-wizard.js +13 -19
  86. package/dist/src/react-router/react-router-wizard.js.map +1 -1
  87. package/dist/src/react-router/sdk-setup.d.ts +2 -2
  88. package/dist/src/react-router/sdk-setup.js +16 -15
  89. package/dist/src/react-router/sdk-setup.js.map +1 -1
  90. package/dist/src/react-router/templates.d.ts +1 -3
  91. package/dist/src/react-router/templates.js +24 -92
  92. package/dist/src/react-router/templates.js.map +1 -1
  93. package/dist/src/remix/sdk-setup.js +1 -2
  94. package/dist/src/remix/sdk-setup.js.map +1 -1
  95. package/dist/src/run.d.ts +4 -1
  96. package/dist/src/run.js +13 -0
  97. package/dist/src/run.js.map +1 -1
  98. package/dist/src/sourcemaps/tools/remix.js +4 -4
  99. package/dist/src/sourcemaps/tools/remix.js.map +1 -1
  100. package/dist/src/sveltekit/sdk-setup/setup.js +17 -4
  101. package/dist/src/sveltekit/sdk-setup/setup.js.map +1 -1
  102. package/dist/src/sveltekit/templates.js +12 -6
  103. package/dist/src/sveltekit/templates.js.map +1 -1
  104. package/dist/src/utils/clack/index.d.ts +2 -1
  105. package/dist/src/utils/clack/index.js +17 -6
  106. package/dist/src/utils/clack/index.js.map +1 -1
  107. package/dist/src/utils/files.d.ts +2 -0
  108. package/dist/src/utils/files.js +58 -0
  109. package/dist/src/utils/files.js.map +1 -0
  110. package/dist/src/utils/git.d.ts +3 -1
  111. package/dist/src/utils/git.js +2 -1
  112. package/dist/src/utils/git.js.map +1 -1
  113. package/dist/src/utils/line-endings.d.ts +1 -0
  114. package/dist/src/utils/line-endings.js +76 -0
  115. package/dist/src/utils/line-endings.js.map +1 -0
  116. package/dist/src/version.d.ts +1 -1
  117. package/dist/src/version.js +1 -1
  118. package/dist/src/version.js.map +1 -1
  119. package/dist/test/angular/angular-wizard.test.js +0 -5
  120. package/dist/test/angular/angular-wizard.test.js.map +1 -1
  121. package/dist/test/apple/lookup-xcode-project.test.js +167 -0
  122. package/dist/test/apple/lookup-xcode-project.test.js.map +1 -0
  123. package/dist/test/apple/snapshots/apple-snapshots-wizard.test.d.ts +1 -0
  124. package/dist/test/apple/snapshots/apple-snapshots-wizard.test.js +487 -0
  125. package/dist/test/apple/snapshots/apple-snapshots-wizard.test.js.map +1 -0
  126. package/dist/test/apple/snapshots/hosted-test-target-fixture.d.ts +24 -0
  127. package/dist/test/apple/snapshots/hosted-test-target-fixture.js +191 -0
  128. package/dist/test/apple/snapshots/hosted-test-target-fixture.js.map +1 -0
  129. package/dist/test/apple/snapshots/snapshot-test-file.test.d.ts +1 -0
  130. package/dist/test/apple/snapshots/snapshot-test-file.test.js +110 -0
  131. package/dist/test/apple/snapshots/snapshot-test-file.test.js.map +1 -0
  132. package/dist/test/apple/snapshots/snapshot-verification-scheme.test.d.ts +1 -0
  133. package/dist/test/apple/snapshots/snapshot-verification-scheme.test.js +146 -0
  134. package/dist/test/apple/snapshots/snapshot-verification-scheme.test.js.map +1 -0
  135. package/dist/test/apple/snapshots/snapshotpreviews-xcode-smoke.test.d.ts +1 -0
  136. package/dist/test/apple/snapshots/snapshotpreviews-xcode-smoke.test.js +186 -0
  137. package/dist/test/apple/snapshots/snapshotpreviews-xcode-smoke.test.js.map +1 -0
  138. package/dist/test/apple/snapshots/snapshots-cli-preflight.test.d.ts +1 -0
  139. package/dist/test/apple/snapshots/snapshots-cli-preflight.test.js +192 -0
  140. package/dist/test/apple/snapshots/snapshots-cli-preflight.test.js.map +1 -0
  141. package/dist/test/apple/snapshots/source-file-insertion.test.d.ts +1 -0
  142. package/dist/test/apple/snapshots/source-file-insertion.test.js +77 -0
  143. package/dist/test/apple/snapshots/source-file-insertion.test.js.map +1 -0
  144. package/dist/test/apple/xcode-manager.test.js +452 -43
  145. package/dist/test/apple/xcode-manager.test.js.map +1 -1
  146. package/dist/test/constants.test.d.ts +1 -0
  147. package/dist/test/constants.test.js +12 -0
  148. package/dist/test/constants.test.js.map +1 -0
  149. package/dist/test/nextjs/templates.test.js +66 -33
  150. package/dist/test/nextjs/templates.test.js.map +1 -1
  151. package/dist/test/nuxt/templates.test.js +66 -36
  152. package/dist/test/nuxt/templates.test.js.map +1 -1
  153. package/dist/test/react-router/codemods/client-entry.test.js +15 -11
  154. package/dist/test/react-router/codemods/client-entry.test.js.map +1 -1
  155. package/dist/test/react-router/codemods/server-entry.test.js +21 -8
  156. package/dist/test/react-router/codemods/server-entry.test.js.map +1 -1
  157. package/dist/test/react-router/sdk-setup.test.js +46 -10
  158. package/dist/test/react-router/sdk-setup.test.js.map +1 -1
  159. package/dist/test/react-router/templates.test.js +26 -64
  160. package/dist/test/react-router/templates.test.js.map +1 -1
  161. package/dist/test/remix/build-script.test.d.ts +1 -0
  162. package/dist/test/remix/build-script.test.js +124 -0
  163. package/dist/test/remix/build-script.test.js.map +1 -0
  164. package/dist/test/remix/client-entry.test.js +4 -10
  165. package/dist/test/remix/client-entry.test.js.map +1 -1
  166. package/dist/test/run.test.d.ts +1 -0
  167. package/dist/test/run.test.js +137 -0
  168. package/dist/test/run.test.js.map +1 -0
  169. package/dist/test/sveltekit/templates.test.js +78 -27
  170. package/dist/test/sveltekit/templates.test.js.map +1 -1
  171. package/dist/test/utils/clack/index.test.js +101 -0
  172. package/dist/test/utils/clack/index.test.js.map +1 -1
  173. package/dist/test/utils/git.test.js +10 -0
  174. package/dist/test/utils/git.test.js.map +1 -1
  175. package/dist/test/utils/line-endings.test.d.ts +1 -0
  176. package/dist/test/utils/line-endings.test.js +103 -0
  177. package/dist/test/utils/line-endings.test.js.map +1 -0
  178. package/package.json +2 -2
  179. package/dist/src/react-router/codemods/root.d.ts +0 -1
  180. package/dist/src/react-router/codemods/root.js +0 -170
  181. package/dist/src/react-router/codemods/root.js.map +0 -1
  182. package/dist/test/react-router/codemods/root.test.js +0 -182
  183. package/dist/test/react-router/codemods/root.test.js.map +0 -1
  184. /package/dist/test/{react-router/codemods/root.test.d.ts → apple/lookup-xcode-project.test.d.ts} +0 -0
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.resolveSnapshotVerificationSchemeName = void 0;
30
+ const fs = __importStar(require("node:fs"));
31
+ const path = __importStar(require("node:path"));
32
+ const xml_js_1 = __importDefault(require("xml-js"));
33
+ const debug_1 = require("../../utils/debug");
34
+ const files_1 = require("../../utils/files");
35
+ function resolveSnapshotVerificationSchemeName({ hostedTestTargetName, xcodeprojPath, }) {
36
+ const explicitSchemes = getExplicitSchemes(xcodeprojPath);
37
+ const matchingExplicitSchemes = explicitSchemes.filter((scheme) => scheme.testTargetNames.includes(hostedTestTargetName));
38
+ const matchingExplicitSchemeNames = uniqueStrings(matchingExplicitSchemes.map((scheme) => scheme.name));
39
+ if (matchingExplicitSchemeNames.length === 1) {
40
+ return matchingExplicitSchemeNames[0];
41
+ }
42
+ const managedSchemeNames = getManagedSchemeNames(xcodeprojPath);
43
+ if (managedSchemeNames.length === 1) {
44
+ return managedSchemeNames[0];
45
+ }
46
+ return undefined;
47
+ }
48
+ exports.resolveSnapshotVerificationSchemeName = resolveSnapshotVerificationSchemeName;
49
+ function getExplicitSchemes(xcodeprojPath) {
50
+ return getSchemeFilePaths(xcodeprojPath).flatMap((schemePath) => {
51
+ const schemeName = path.basename(schemePath, '.xcscheme');
52
+ const testTargetNames = readSchemeTestTargetNames(schemePath);
53
+ return schemeName ? [{ name: schemeName, testTargetNames }] : [];
54
+ });
55
+ }
56
+ function getSchemeFilePaths(xcodeprojPath) {
57
+ return [
58
+ path.join(xcodeprojPath, 'xcshareddata', 'xcschemes'),
59
+ path.join(xcodeprojPath, 'xcuserdata'),
60
+ ].flatMap((schemeDirectory) => (0, files_1.findFilesWithExtension)(schemeDirectory, '.xcscheme'));
61
+ }
62
+ function readSchemeTestTargetNames(schemePath) {
63
+ const scheme = readXmlFile(schemePath);
64
+ if (!scheme) {
65
+ return [];
66
+ }
67
+ const testAction = getChild(getChild(scheme, 'Scheme'), 'TestAction');
68
+ const targetNames = new Set();
69
+ collectBlueprintNames(testAction, targetNames);
70
+ return [...targetNames];
71
+ }
72
+ function getManagedSchemeNames(xcodeprojPath) {
73
+ return uniqueStrings((0, files_1.findFilesWithName)(xcodeprojPath, 'xcschememanagement.plist').flatMap(readManagedSchemeNames));
74
+ }
75
+ function readManagedSchemeNames(plistPath) {
76
+ const plist = readXmlFile(plistPath);
77
+ if (!plist) {
78
+ return [];
79
+ }
80
+ const schemeNames = [];
81
+ collectElementText(plist, 'key').forEach((key) => {
82
+ const schemeName = parseManagedSchemeName(key);
83
+ if (schemeName) {
84
+ schemeNames.push(schemeName);
85
+ }
86
+ });
87
+ return schemeNames;
88
+ }
89
+ function parseManagedSchemeName(key) {
90
+ return /^(.+)\.xcscheme(?:_.+)?$/.exec(key)?.[1];
91
+ }
92
+ function readXmlFile(filePath) {
93
+ try {
94
+ return xml_js_1.default.xml2js(fs.readFileSync(filePath, 'utf8'), {
95
+ compact: true,
96
+ });
97
+ }
98
+ catch (error) {
99
+ (0, debug_1.debug)('Could not read XML file:', filePath, error);
100
+ return undefined;
101
+ }
102
+ }
103
+ function getChild(node, childName) {
104
+ return isRecord(node) ? node[childName] : undefined;
105
+ }
106
+ function collectBlueprintNames(node, targetNames) {
107
+ if (Array.isArray(node)) {
108
+ node.forEach((item) => collectBlueprintNames(item, targetNames));
109
+ return;
110
+ }
111
+ if (!isRecord(node)) {
112
+ return;
113
+ }
114
+ const attributes = node._attributes;
115
+ if (isRecord(attributes) && typeof attributes.BlueprintName === 'string') {
116
+ targetNames.add(attributes.BlueprintName);
117
+ }
118
+ Object.values(node).forEach((value) => collectBlueprintNames(value, targetNames));
119
+ }
120
+ function collectElementText(node, elementName) {
121
+ if (Array.isArray(node)) {
122
+ return node.flatMap((item) => collectElementText(item, elementName));
123
+ }
124
+ if (!isRecord(node)) {
125
+ return [];
126
+ }
127
+ return Object.entries(node).flatMap(([key, value]) => {
128
+ const childText = key === elementName ? collectTextValues(value) : [];
129
+ return childText.concat(collectElementText(value, elementName));
130
+ });
131
+ }
132
+ function collectTextValues(node) {
133
+ if (Array.isArray(node)) {
134
+ return node.flatMap(collectTextValues);
135
+ }
136
+ if (!isRecord(node)) {
137
+ return [];
138
+ }
139
+ return typeof node._text === 'string' ? [node._text] : [];
140
+ }
141
+ function uniqueStrings(values) {
142
+ return [...new Set(values)];
143
+ }
144
+ function isRecord(value) {
145
+ return typeof value === 'object' && value !== null;
146
+ }
147
+ //# sourceMappingURL=snapshot-verification-scheme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot-verification-scheme.js","sourceRoot":"","sources":["../../../../src/apple/snapshots/snapshot-verification-scheme.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport xml from 'xml-js';\n\nimport { debug } from '../../utils/debug';\nimport { findFilesWithExtension, findFilesWithName } from '../../utils/files';\n\ntype SnapshotVerificationSchemeOptions = {\n hostedTestTargetName: string;\n xcodeprojPath: string;\n};\n\ntype ExplicitScheme = {\n name: string;\n testTargetNames: string[];\n};\n\nexport function resolveSnapshotVerificationSchemeName({\n hostedTestTargetName,\n xcodeprojPath,\n}: SnapshotVerificationSchemeOptions): string | undefined {\n const explicitSchemes = getExplicitSchemes(xcodeprojPath);\n const matchingExplicitSchemes = explicitSchemes.filter((scheme) =>\n scheme.testTargetNames.includes(hostedTestTargetName),\n );\n const matchingExplicitSchemeNames = uniqueStrings(\n matchingExplicitSchemes.map((scheme) => scheme.name),\n );\n if (matchingExplicitSchemeNames.length === 1) {\n return matchingExplicitSchemeNames[0];\n }\n\n const managedSchemeNames = getManagedSchemeNames(xcodeprojPath);\n if (managedSchemeNames.length === 1) {\n return managedSchemeNames[0];\n }\n\n return undefined;\n}\n\nfunction getExplicitSchemes(xcodeprojPath: string): ExplicitScheme[] {\n return getSchemeFilePaths(xcodeprojPath).flatMap((schemePath) => {\n const schemeName = path.basename(schemePath, '.xcscheme');\n const testTargetNames = readSchemeTestTargetNames(schemePath);\n return schemeName ? [{ name: schemeName, testTargetNames }] : [];\n });\n}\n\nfunction getSchemeFilePaths(xcodeprojPath: string): string[] {\n return [\n path.join(xcodeprojPath, 'xcshareddata', 'xcschemes'),\n path.join(xcodeprojPath, 'xcuserdata'),\n ].flatMap((schemeDirectory) =>\n findFilesWithExtension(schemeDirectory, '.xcscheme'),\n );\n}\n\nfunction readSchemeTestTargetNames(schemePath: string): string[] {\n const scheme = readXmlFile(schemePath);\n if (!scheme) {\n return [];\n }\n\n const testAction = getChild(getChild(scheme, 'Scheme'), 'TestAction');\n const targetNames = new Set<string>();\n collectBlueprintNames(testAction, targetNames);\n return [...targetNames];\n}\n\nfunction getManagedSchemeNames(xcodeprojPath: string): string[] {\n return uniqueStrings(\n findFilesWithName(xcodeprojPath, 'xcschememanagement.plist').flatMap(\n readManagedSchemeNames,\n ),\n );\n}\n\nfunction readManagedSchemeNames(plistPath: string): string[] {\n const plist = readXmlFile(plistPath);\n if (!plist) {\n return [];\n }\n\n const schemeNames: string[] = [];\n collectElementText(plist, 'key').forEach((key) => {\n const schemeName = parseManagedSchemeName(key);\n if (schemeName) {\n schemeNames.push(schemeName);\n }\n });\n return schemeNames;\n}\n\nfunction parseManagedSchemeName(key: string): string | undefined {\n return /^(.+)\\.xcscheme(?:_.+)?$/.exec(key)?.[1];\n}\n\nfunction readXmlFile(filePath: string): unknown | undefined {\n try {\n return xml.xml2js(fs.readFileSync(filePath, 'utf8'), {\n compact: true,\n });\n } catch (error) {\n debug('Could not read XML file:', filePath, error);\n return undefined;\n }\n}\n\nfunction getChild(node: unknown, childName: string): unknown {\n return isRecord(node) ? node[childName] : undefined;\n}\n\nfunction collectBlueprintNames(node: unknown, targetNames: Set<string>): void {\n if (Array.isArray(node)) {\n node.forEach((item) => collectBlueprintNames(item, targetNames));\n return;\n }\n\n if (!isRecord(node)) {\n return;\n }\n\n const attributes = node._attributes;\n if (isRecord(attributes) && typeof attributes.BlueprintName === 'string') {\n targetNames.add(attributes.BlueprintName);\n }\n\n Object.values(node).forEach((value) =>\n collectBlueprintNames(value, targetNames),\n );\n}\n\nfunction collectElementText(node: unknown, elementName: string): string[] {\n if (Array.isArray(node)) {\n return node.flatMap((item) => collectElementText(item, elementName));\n }\n\n if (!isRecord(node)) {\n return [];\n }\n\n return Object.entries(node).flatMap(([key, value]) => {\n const childText = key === elementName ? collectTextValues(value) : [];\n return childText.concat(collectElementText(value, elementName));\n });\n}\n\nfunction collectTextValues(node: unknown): string[] {\n if (Array.isArray(node)) {\n return node.flatMap(collectTextValues);\n }\n\n if (!isRecord(node)) {\n return [];\n }\n\n return typeof node._text === 'string' ? [node._text] : [];\n}\n\nfunction uniqueStrings(values: string[]): string[] {\n return [...new Set(values)];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n"]}
@@ -0,0 +1,4 @@
1
+ export declare const SNAPSHOTPREVIEWS_PACKAGE_URL = "https://github.com/getsentry/SnapshotPreviews";
2
+ export declare const SNAPSHOTPREVIEWS_MINIMUM_VERSION = "0.17.0";
3
+ export declare const SNAPSHOTPREVIEWS_SNAPSHOT_TESTS_PRODUCT = "SnapshottingTests";
4
+ export declare const SNAPSHOTPREVIEWS_PREFERENCES_PRODUCT = "SnapshotPreferences";
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SNAPSHOTPREVIEWS_PREFERENCES_PRODUCT = exports.SNAPSHOTPREVIEWS_SNAPSHOT_TESTS_PRODUCT = exports.SNAPSHOTPREVIEWS_MINIMUM_VERSION = exports.SNAPSHOTPREVIEWS_PACKAGE_URL = void 0;
4
+ exports.SNAPSHOTPREVIEWS_PACKAGE_URL = 'https://github.com/getsentry/SnapshotPreviews';
5
+ exports.SNAPSHOTPREVIEWS_MINIMUM_VERSION = '0.17.0';
6
+ exports.SNAPSHOTPREVIEWS_SNAPSHOT_TESTS_PRODUCT = 'SnapshottingTests';
7
+ exports.SNAPSHOTPREVIEWS_PREFERENCES_PRODUCT = 'SnapshotPreferences';
8
+ //# sourceMappingURL=snapshotpreviews-package.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshotpreviews-package.js","sourceRoot":"","sources":["../../../../src/apple/snapshots/snapshotpreviews-package.ts"],"names":[],"mappings":";;;AAAa,QAAA,4BAA4B,GACvC,+CAA+C,CAAC;AAErC,QAAA,gCAAgC,GAAG,QAAQ,CAAC;AAE5C,QAAA,uCAAuC,GAAG,mBAAmB,CAAC;AAE9D,QAAA,oCAAoC,GAAG,qBAAqB,CAAC","sourcesContent":["export const SNAPSHOTPREVIEWS_PACKAGE_URL =\n 'https://github.com/getsentry/SnapshotPreviews';\n\nexport const SNAPSHOTPREVIEWS_MINIMUM_VERSION = '0.17.0';\n\nexport const SNAPSHOTPREVIEWS_SNAPSHOT_TESTS_PRODUCT = 'SnapshottingTests';\n\nexport const SNAPSHOTPREVIEWS_PREFERENCES_PRODUCT = 'SnapshotPreferences';\n"]}
@@ -0,0 +1,23 @@
1
+ type SnapshotVerificationGuidance = {
2
+ appId?: string;
3
+ hostedTestTargetName: string;
4
+ projectDir: string;
5
+ projectPath: string;
6
+ schemeName?: string;
7
+ snapshotTestClassName: string;
8
+ };
9
+ export declare function checkInstalledCLISnapshots({ projectDir, nonInteractive, verificationGuidance, }: {
10
+ projectDir: string;
11
+ nonInteractive: boolean;
12
+ verificationGuidance: SnapshotVerificationGuidance;
13
+ }): Promise<void>;
14
+ export declare function getSnapshotFastlaneGuidance(projectDir: string): {
15
+ hasUploadLane: boolean;
16
+ hasSentryUploadAction: boolean;
17
+ fastfilePath?: undefined;
18
+ } | {
19
+ fastfilePath: string;
20
+ hasUploadLane: boolean;
21
+ hasSentryUploadAction: boolean;
22
+ };
23
+ export {};
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.getSnapshotFastlaneGuidance = exports.checkInstalledCLISnapshots = void 0;
30
+ const fs = __importStar(require("node:fs"));
31
+ const path = __importStar(require("node:path"));
32
+ // @ts-expect-error - clack is ESM and TS complains about that. It works though
33
+ const prompts_1 = __importDefault(require("@clack/prompts"));
34
+ const Sentry = __importStar(require("@sentry/node"));
35
+ const bash = __importStar(require("../../utils/bash"));
36
+ const check_installed_cli_1 = require("../check-installed-cli");
37
+ const fastlane = __importStar(require("../fastlane"));
38
+ async function checkInstalledCLISnapshots({ projectDir, nonInteractive, verificationGuidance, }) {
39
+ const hasCli = await (0, check_installed_cli_1.checkInstalledCLI)("Without sentry-cli, you won't be able to upload snapshots to Sentry. You can install it later by following the instructions at https://docs.sentry.io/cli/", nonInteractive);
40
+ if (hasCli) {
41
+ verifySnapshotsUploadCommand();
42
+ }
43
+ printSnapshotsUploadGuidance({
44
+ fastlaneGuidance: getSnapshotFastlaneGuidance(projectDir),
45
+ verificationGuidance,
46
+ });
47
+ }
48
+ exports.checkInstalledCLISnapshots = checkInstalledCLISnapshots;
49
+ function getSnapshotFastlaneGuidance(projectDir) {
50
+ const fastfilePath = fastlane.fastFile(projectDir) ?? undefined;
51
+ if (!fastfilePath) {
52
+ return {
53
+ hasUploadLane: false,
54
+ hasSentryUploadAction: false,
55
+ };
56
+ }
57
+ let contents;
58
+ try {
59
+ contents = fs.readFileSync(fastfilePath, 'utf8');
60
+ }
61
+ catch {
62
+ prompts_1.default.log.warn(`Could not read the Fastfile at ${fastfilePath}. Skipping Fastlane snapshots upload detection.`);
63
+ return {
64
+ hasUploadLane: false,
65
+ hasSentryUploadAction: false,
66
+ };
67
+ }
68
+ return {
69
+ fastfilePath,
70
+ hasUploadLane: /lane\s+:upload_sentry_snapshots\b/.test(contents),
71
+ hasSentryUploadAction: /\bsentry_upload_snapshots\s*\(/.test(contents),
72
+ };
73
+ }
74
+ exports.getSnapshotFastlaneGuidance = getSnapshotFastlaneGuidance;
75
+ function verifySnapshotsUploadCommand() {
76
+ try {
77
+ bash.executeSync('sentry-cli snapshots upload --help');
78
+ Sentry.setTag('sentry-cli-snapshots-upload', true);
79
+ prompts_1.default.log.success('sentry-cli snapshots upload is available.');
80
+ }
81
+ catch {
82
+ Sentry.setTag('sentry-cli-snapshots-upload', false);
83
+ prompts_1.default.log.warn('sentry-cli is installed, but this version does not expose `sentry-cli snapshots upload`. Please update sentry-cli before uploading snapshots.');
84
+ }
85
+ }
86
+ function printSnapshotsUploadGuidance({ fastlaneGuidance, verificationGuidance, }) {
87
+ if (fastlaneGuidance.fastfilePath && fastlaneGuidance.hasUploadLane) {
88
+ prompts_1.default.log.info([
89
+ 'Detected existing Fastlane snapshots upload support.',
90
+ buildSnapshotExportCommand(verificationGuidance),
91
+ 'Then verify upload with:',
92
+ 'bundle exec fastlane ios upload_sentry_snapshots path: "$PWD/snapshot-images"',
93
+ ].join('\n'));
94
+ return;
95
+ }
96
+ if (fastlaneGuidance.fastfilePath && fastlaneGuidance.hasSentryUploadAction) {
97
+ prompts_1.default.log.info([
98
+ 'Detected a Fastlane lane that calls sentry_upload_snapshots.',
99
+ 'After exporting snapshots, run that existing lane with your snapshot image path.',
100
+ 'If you are unsure which lane to use, fall back to sentry-cli snapshots upload below.',
101
+ ].join('\n'));
102
+ }
103
+ prompts_1.default.log.info([
104
+ buildSnapshotExportCommand(verificationGuidance),
105
+ '',
106
+ 'After SnapshotPreviews exports images, verify local upload with:',
107
+ 'sentry-cli snapshots upload "$PWD/snapshot-images" \\',
108
+ ' --org your-org \\',
109
+ ' --project your-ios-project \\',
110
+ verificationGuidance.appId
111
+ ? ` --app-id ${verificationGuidance.appId}`
112
+ : ' --app-id YOUR_APP_BUNDLE_ID # replace with your app bundle identifier',
113
+ '',
114
+ 'Auth comes from SENTRY_AUTH_TOKEN or --auth-token. CI workflow setup is documented at https://docs.sentry.io/platforms/apple/snapshots/#step-3-integrate-into-ci; this wizard does not write workflow files.',
115
+ ].join('\n'));
116
+ }
117
+ function buildSnapshotExportCommand(guidance) {
118
+ return [
119
+ `From ${quoteShell(guidance.projectDir)}, export snapshots with:`,
120
+ 'TEST_RUNNER_SNAPSHOTS_EXPORT_DIR="$PWD/snapshot-images" \\',
121
+ 'xcodebuild test \\',
122
+ ` -project ${quoteShell(path.relative(guidance.projectDir, guidance.projectPath) ||
123
+ guidance.projectPath)} \\`,
124
+ ` -scheme ${guidance.schemeName ? quoteShell(guidance.schemeName) : '<scheme>'} \\`,
125
+ " -destination 'platform=iOS Simulator,name=iPhone 17 Pro' \\",
126
+ ` -only-testing:${quoteShell(`${guidance.hostedTestTargetName}/${guidance.snapshotTestClassName}`)} \\`,
127
+ ' CODE_SIGNING_ALLOWED=NO \\',
128
+ ' | xcpretty',
129
+ ].join('\n');
130
+ }
131
+ function quoteShell(value) {
132
+ return /^[A-Za-z0-9_./:-]+$/.test(value)
133
+ ? value
134
+ : `'${value.replace(/'/g, "'\\''")}'`;
135
+ }
136
+ //# sourceMappingURL=snapshots-cli-preflight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshots-cli-preflight.js","sourceRoot":"","sources":["../../../../src/apple/snapshots/snapshots-cli-preflight.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n// @ts-expect-error - clack is ESM and TS complains about that. It works though\nimport clack from '@clack/prompts';\nimport * as Sentry from '@sentry/node';\nimport * as bash from '../../utils/bash';\nimport { checkInstalledCLI } from '../check-installed-cli';\nimport * as fastlane from '../fastlane';\n\ntype SnapshotVerificationGuidance = {\n appId?: string;\n hostedTestTargetName: string;\n projectDir: string;\n projectPath: string;\n schemeName?: string;\n snapshotTestClassName: string;\n};\n\nexport async function checkInstalledCLISnapshots({\n projectDir,\n nonInteractive,\n verificationGuidance,\n}: {\n projectDir: string;\n nonInteractive: boolean;\n verificationGuidance: SnapshotVerificationGuidance;\n}): Promise<void> {\n const hasCli = await checkInstalledCLI(\n \"Without sentry-cli, you won't be able to upload snapshots to Sentry. You can install it later by following the instructions at https://docs.sentry.io/cli/\",\n nonInteractive,\n );\n\n if (hasCli) {\n verifySnapshotsUploadCommand();\n }\n\n printSnapshotsUploadGuidance({\n fastlaneGuidance: getSnapshotFastlaneGuidance(projectDir),\n verificationGuidance,\n });\n}\n\nexport function getSnapshotFastlaneGuidance(projectDir: string) {\n const fastfilePath = fastlane.fastFile(projectDir) ?? undefined;\n if (!fastfilePath) {\n return {\n hasUploadLane: false,\n hasSentryUploadAction: false,\n };\n }\n\n let contents: string;\n try {\n contents = fs.readFileSync(fastfilePath, 'utf8');\n } catch {\n clack.log.warn(\n `Could not read the Fastfile at ${fastfilePath}. Skipping Fastlane snapshots upload detection.`,\n );\n return {\n hasUploadLane: false,\n hasSentryUploadAction: false,\n };\n }\n\n return {\n fastfilePath,\n hasUploadLane: /lane\\s+:upload_sentry_snapshots\\b/.test(contents),\n hasSentryUploadAction: /\\bsentry_upload_snapshots\\s*\\(/.test(contents),\n };\n}\n\nfunction verifySnapshotsUploadCommand(): void {\n try {\n bash.executeSync('sentry-cli snapshots upload --help');\n Sentry.setTag('sentry-cli-snapshots-upload', true);\n clack.log.success('sentry-cli snapshots upload is available.');\n } catch {\n Sentry.setTag('sentry-cli-snapshots-upload', false);\n clack.log.warn(\n 'sentry-cli is installed, but this version does not expose `sentry-cli snapshots upload`. Please update sentry-cli before uploading snapshots.',\n );\n }\n}\n\nfunction printSnapshotsUploadGuidance({\n fastlaneGuidance,\n verificationGuidance,\n}: {\n fastlaneGuidance: ReturnType<typeof getSnapshotFastlaneGuidance>;\n verificationGuidance: SnapshotVerificationGuidance;\n}): void {\n if (fastlaneGuidance.fastfilePath && fastlaneGuidance.hasUploadLane) {\n clack.log.info(\n [\n 'Detected existing Fastlane snapshots upload support.',\n buildSnapshotExportCommand(verificationGuidance),\n 'Then verify upload with:',\n 'bundle exec fastlane ios upload_sentry_snapshots path: \"$PWD/snapshot-images\"',\n ].join('\\n'),\n );\n return;\n }\n\n if (fastlaneGuidance.fastfilePath && fastlaneGuidance.hasSentryUploadAction) {\n clack.log.info(\n [\n 'Detected a Fastlane lane that calls sentry_upload_snapshots.',\n 'After exporting snapshots, run that existing lane with your snapshot image path.',\n 'If you are unsure which lane to use, fall back to sentry-cli snapshots upload below.',\n ].join('\\n'),\n );\n }\n\n clack.log.info(\n [\n buildSnapshotExportCommand(verificationGuidance),\n '',\n 'After SnapshotPreviews exports images, verify local upload with:',\n 'sentry-cli snapshots upload \"$PWD/snapshot-images\" \\\\',\n ' --org your-org \\\\',\n ' --project your-ios-project \\\\',\n verificationGuidance.appId\n ? ` --app-id ${verificationGuidance.appId}`\n : ' --app-id YOUR_APP_BUNDLE_ID # replace with your app bundle identifier',\n '',\n 'Auth comes from SENTRY_AUTH_TOKEN or --auth-token. CI workflow setup is documented at https://docs.sentry.io/platforms/apple/snapshots/#step-3-integrate-into-ci; this wizard does not write workflow files.',\n ].join('\\n'),\n );\n}\n\nfunction buildSnapshotExportCommand(\n guidance: SnapshotVerificationGuidance,\n): string {\n return [\n `From ${quoteShell(guidance.projectDir)}, export snapshots with:`,\n 'TEST_RUNNER_SNAPSHOTS_EXPORT_DIR=\"$PWD/snapshot-images\" \\\\',\n 'xcodebuild test \\\\',\n ` -project ${quoteShell(\n path.relative(guidance.projectDir, guidance.projectPath) ||\n guidance.projectPath,\n )} \\\\`,\n ` -scheme ${\n guidance.schemeName ? quoteShell(guidance.schemeName) : '<scheme>'\n } \\\\`,\n \" -destination 'platform=iOS Simulator,name=iPhone 17 Pro' \\\\\",\n ` -only-testing:${quoteShell(\n `${guidance.hostedTestTargetName}/${guidance.snapshotTestClassName}`,\n )} \\\\`,\n ' CODE_SIGNING_ALLOWED=NO \\\\',\n ' | xcpretty',\n ].join('\\n');\n}\n\nfunction quoteShell(value: string): string {\n return /^[A-Za-z0-9_./:-]+$/.test(value)\n ? value\n : `'${value.replace(/'/g, \"'\\\\''\")}'`;\n}\n"]}
@@ -1,6 +1,23 @@
1
1
  import type { SentryProjectData } from '../utils/types';
2
2
  import { PBXSourcesBuildPhase, type PBXNativeTarget, type PBXObjects, type Project } from 'xcode';
3
3
  import { XcodeProjectObjectWithId } from './xcode-project-object-with-id';
4
+ export type SwiftPackageSpec = {
5
+ repositoryURL: string;
6
+ requirement: {
7
+ kind: 'upToNextMajorVersion';
8
+ minimumVersion: string;
9
+ };
10
+ commentName: string;
11
+ };
12
+ export type SwiftPackageProductSpec = {
13
+ package: SwiftPackageSpec;
14
+ productName: string;
15
+ };
16
+ export type SwiftPackageProductLinkOptions = {
17
+ product: SwiftPackageProductSpec;
18
+ existingFrameworkComment?: string;
19
+ successMessage?: string;
20
+ };
4
21
  export declare class XcodeProject {
5
22
  /**
6
23
  * The directory where the Xcode project is located.
@@ -26,12 +43,38 @@ export declare class XcodeProject {
26
43
  */
27
44
  constructor(projectPath: string);
28
45
  getAllTargets(): string[];
29
- updateXcodeProject(sentryProject: SentryProjectData, target: string, addSPMReference: boolean, uploadSource?: boolean): void;
46
+ getUnitTestTargetNames(): string[];
47
+ getHostedUnitTestTargetNames(): string[];
48
+ getHostedUnitTestTargetNamesForApplicationTarget(appTargetName: string): string[];
49
+ getBundleIdentifierForTarget(targetName: string): string | undefined;
50
+ /**
51
+ * Idempotently links a Swift package product to one target dependency list
52
+ * and Frameworks build phase. Returns whether the pbxproj graph changed and
53
+ * whether the product is linked after the operation.
54
+ */
55
+ ensureSwiftPackageProductLinked(targetName: string, product: SwiftPackageProductSpec): {
56
+ changed: boolean;
57
+ linked: boolean;
58
+ };
59
+ updateXcodeProject(sentryProject: SentryProjectData, target: string, swiftPackageProduct?: SwiftPackageProductLinkOptions, uploadSource?: boolean): void;
30
60
  addUploadSymbolsScript({ sentryProject, targetName, uploadSource, }: {
31
61
  sentryProject: SentryProjectData;
32
62
  targetName: string;
33
63
  uploadSource: boolean;
34
64
  }): void;
65
+ write(): void;
66
+ getSynchronizedRootGroupPathsForTarget(targetName: string): string[];
67
+ /**
68
+ * Ensures a Swift file is compiled by a target, either through an Xcode
69
+ * synchronized root group or an explicit Sources build phase entry.
70
+ */
71
+ addSwiftSourceFileToTarget(args: {
72
+ targetName: string;
73
+ filePath: string;
74
+ }): {
75
+ changed: boolean;
76
+ included: boolean;
77
+ };
35
78
  /**
36
79
  * Retrieves all source files associated with a specific target in the Xcode project.
37
80
  * This is used to find files where we can inject Sentry initialization code.
@@ -40,6 +83,21 @@ export declare class XcodeProject {
40
83
  * @returns An array of absolute file paths for the target's source files, or undefined if target not found
41
84
  */
42
85
  getSourceFilesForTarget(targetName: string): string[] | undefined;
86
+ private isUnitTestTargetEntry;
87
+ private getTargetBuildSettings;
88
+ private getApplicationHostCandidates;
89
+ private getProductReferencePath;
90
+ private hasFrameworkBuildFileCommentInTarget;
91
+ private ensureSwiftPackageReference;
92
+ private ensureProjectSwiftPackageReference;
93
+ private ensureSwiftPackageProductDependency;
94
+ private ensureFrameworksBuildFile;
95
+ private findFrameworksBuildPhaseInTarget;
96
+ private swiftPackageReferenceComment;
97
+ private isFileIncludedBySynchronizedRootGroup;
98
+ private ensureSwiftFileReference;
99
+ private addFileReferenceToBestGroup;
100
+ private get mainGroup();
43
101
  /**
44
102
  * Finds a native target by name.
45
103
  *