@sentry/wizard 3.11.0 → 3.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.
- package/CHANGELOG.md +20 -0
- package/dist/lib/Steps/ChooseIntegration.js +1 -0
- package/dist/lib/Steps/ChooseIntegration.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/src/android/android-wizard.js +14 -4
- package/dist/src/android/android-wizard.js.map +1 -1
- package/dist/src/android/code-tools.d.ts +8 -0
- package/dist/src/android/code-tools.js +20 -8
- package/dist/src/android/code-tools.js.map +1 -1
- package/dist/src/android/gradle.js +6 -1
- package/dist/src/android/gradle.js.map +1 -1
- package/dist/src/nextjs/nextjs-wizard.js +5 -2
- package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
- package/dist/src/nextjs/templates.d.ts +1 -1
- package/dist/src/nextjs/templates.js +2 -2
- package/dist/src/nextjs/templates.js.map +1 -1
- package/dist/src/remix/remix-wizard.js +8 -4
- package/dist/src/remix/remix-wizard.js.map +1 -1
- package/dist/src/remix/sdk-setup.d.ts +5 -1
- package/dist/src/remix/sdk-setup.js +3 -2
- package/dist/src/remix/sdk-setup.js.map +1 -1
- package/dist/src/sourcemaps/tools/sentry-cli.d.ts +9 -0
- package/dist/src/sourcemaps/tools/sentry-cli.js +26 -22
- package/dist/src/sourcemaps/tools/sentry-cli.js.map +1 -1
- package/dist/src/sourcemaps/tools/tsc.d.ts +6 -0
- package/dist/src/sourcemaps/tools/tsc.js +98 -17
- package/dist/src/sourcemaps/tools/tsc.js.map +1 -1
- package/dist/src/sourcemaps/tools/vite.js +39 -124
- package/dist/src/sourcemaps/tools/vite.js.map +1 -1
- package/dist/src/sourcemaps/tools/webpack.d.ts +6 -1
- package/dist/src/sourcemaps/tools/webpack.js +280 -25
- package/dist/src/sourcemaps/tools/webpack.js.map +1 -1
- package/dist/src/sveltekit/sdk-setup.js +123 -49
- package/dist/src/sveltekit/sdk-setup.js.map +1 -1
- package/dist/src/sveltekit/sveltekit-wizard.d.ts +1 -0
- package/dist/src/sveltekit/sveltekit-wizard.js +119 -44
- package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
- package/dist/src/sveltekit/utils.d.ts +2 -0
- package/dist/src/sveltekit/utils.js +48 -0
- package/dist/src/sveltekit/utils.js.map +1 -0
- package/dist/src/utils/ast-utils.d.ts +77 -3
- package/dist/src/utils/ast-utils.js +172 -6
- package/dist/src/utils/ast-utils.js.map +1 -1
- package/dist/src/utils/clack-utils.d.ts +85 -1
- package/dist/src/utils/clack-utils.js +214 -51
- package/dist/src/utils/clack-utils.js.map +1 -1
- package/dist/src/utils/package-manager.d.ts +5 -0
- package/dist/src/utils/package-manager.js +11 -7
- package/dist/src/utils/package-manager.js.map +1 -1
- package/dist/test/android/code-tools.test.d.ts +1 -0
- package/dist/test/android/code-tools.test.js +34 -0
- package/dist/test/android/code-tools.test.js.map +1 -0
- package/dist/test/sourcemaps/tools/sentry-cli.test.d.ts +1 -0
- package/dist/test/sourcemaps/tools/sentry-cli.test.js +112 -0
- package/dist/test/sourcemaps/tools/sentry-cli.test.js.map +1 -0
- package/dist/test/sourcemaps/tools/tsc.test.d.ts +1 -0
- package/dist/test/sourcemaps/tools/tsc.test.js +121 -0
- package/dist/test/sourcemaps/tools/tsc.test.js.map +1 -0
- package/dist/test/sourcemaps/tools/webpack.test.d.ts +1 -0
- package/dist/test/sourcemaps/tools/webpack.test.js +179 -0
- package/dist/test/sourcemaps/tools/webpack.test.js.map +1 -0
- package/dist/test/utils/ast-utils.test.js +181 -15
- package/dist/test/utils/ast-utils.test.js.map +1 -1
- package/dist/test/utils/clack-utils.test.d.ts +1 -0
- package/dist/test/utils/clack-utils.test.js +200 -0
- package/dist/test/utils/clack-utils.test.js.map +1 -0
- package/lib/Steps/ChooseIntegration.ts +1 -0
- package/package.json +1 -1
- package/src/android/android-wizard.ts +16 -5
- package/src/android/code-tools.ts +21 -7
- package/src/android/gradle.ts +6 -1
- package/src/nextjs/nextjs-wizard.ts +15 -3
- package/src/nextjs/templates.ts +3 -2
- package/src/remix/remix-wizard.ts +8 -11
- package/src/remix/sdk-setup.ts +8 -2
- package/src/sourcemaps/tools/sentry-cli.ts +16 -9
- package/src/sourcemaps/tools/tsc.ts +133 -28
- package/src/sourcemaps/tools/vite.ts +37 -127
- package/src/sourcemaps/tools/webpack.ts +343 -27
- package/src/sveltekit/sdk-setup.ts +115 -39
- package/src/sveltekit/sveltekit-wizard.ts +93 -25
- package/src/sveltekit/utils.ts +50 -0
- package/src/utils/ast-utils.ts +203 -7
- package/src/utils/clack-utils.ts +211 -44
- package/src/utils/package-manager.ts +12 -6
- package/test/android/code-tools.test.ts +49 -0
- package/test/sourcemaps/tools/sentry-cli.test.ts +51 -0
- package/test/sourcemaps/tools/tsc.test.ts +181 -0
- package/test/sourcemaps/tools/webpack.test.ts +303 -0
- package/test/utils/ast-utils.test.ts +240 -20
- package/test/utils/clack-utils.test.ts +142 -0
package/src/utils/clack-utils.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
installPackageWithPackageManager,
|
|
18
18
|
packageManagers,
|
|
19
19
|
} from './package-manager';
|
|
20
|
+
import { debug } from './debug';
|
|
20
21
|
|
|
21
22
|
const opn = require('opn') as (
|
|
22
23
|
url: string,
|
|
@@ -124,18 +125,23 @@ export function printWelcome(options: {
|
|
|
124
125
|
|
|
125
126
|
let welcomeText =
|
|
126
127
|
options.message ||
|
|
127
|
-
|
|
128
|
+
`The ${options.wizardName} will help you set up Sentry for your application.\nThank you for using Sentry :)`;
|
|
128
129
|
|
|
129
130
|
if (options.promoCode) {
|
|
130
|
-
welcomeText
|
|
131
|
+
welcomeText = `${welcomeText}\n\nUsing promo-code: ${options.promoCode}`;
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
if (wizardPackage.version) {
|
|
134
|
-
welcomeText
|
|
135
|
+
welcomeText = `${welcomeText}\n\nVersion: ${wizardPackage.version}`;
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
if (options.telemetryEnabled) {
|
|
138
|
-
welcomeText
|
|
139
|
+
welcomeText = `${welcomeText}
|
|
140
|
+
|
|
141
|
+
This wizard sends telemetry data and crash reports to Sentry. This helps us improve the Wizard.
|
|
142
|
+
You can turn this off at any time by running ${chalk.cyanBright(
|
|
143
|
+
'sentry-wizard --disable-telemetry',
|
|
144
|
+
)}.`;
|
|
139
145
|
}
|
|
140
146
|
|
|
141
147
|
clack.note(welcomeText);
|
|
@@ -247,43 +253,9 @@ export async function installPackage({
|
|
|
247
253
|
);
|
|
248
254
|
}
|
|
249
255
|
|
|
250
|
-
async function addOrgAndProjectToSentryCliRc(
|
|
251
|
-
org: string,
|
|
252
|
-
project: string,
|
|
253
|
-
setupConfig: CliSetupConfig,
|
|
254
|
-
): Promise<void> {
|
|
255
|
-
const configContents = fs.readFileSync(
|
|
256
|
-
path.join(process.cwd(), setupConfig.filename),
|
|
257
|
-
'utf8',
|
|
258
|
-
);
|
|
259
|
-
|
|
260
|
-
if (setupConfig.likelyAlreadyHasOrgAndProject(configContents)) {
|
|
261
|
-
clack.log.warn(
|
|
262
|
-
`${chalk.bold(
|
|
263
|
-
setupConfig.filename,
|
|
264
|
-
)} already has org and project. Will not add them.`,
|
|
265
|
-
);
|
|
266
|
-
} else {
|
|
267
|
-
try {
|
|
268
|
-
await fs.promises.appendFile(
|
|
269
|
-
path.join(process.cwd(), setupConfig.filename),
|
|
270
|
-
`\n${setupConfig.orgAndProjContent(org, project)}\n`,
|
|
271
|
-
);
|
|
272
|
-
} catch (e) {
|
|
273
|
-
clack.log.warn(
|
|
274
|
-
`${chalk.bold(
|
|
275
|
-
setupConfig.filename,
|
|
276
|
-
)} could not be updated with org and project.`,
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
256
|
export async function addSentryCliConfig(
|
|
283
257
|
authToken: string,
|
|
284
258
|
setupConfig: CliSetupConfig = sourceMapsCliSetupConfig,
|
|
285
|
-
orgSlug?: string,
|
|
286
|
-
projectSlug?: string,
|
|
287
259
|
): Promise<void> {
|
|
288
260
|
const configExists = fs.existsSync(
|
|
289
261
|
path.join(process.cwd(), setupConfig.filename),
|
|
@@ -351,10 +323,6 @@ export async function addSentryCliConfig(
|
|
|
351
323
|
}
|
|
352
324
|
}
|
|
353
325
|
|
|
354
|
-
if (orgSlug && projectSlug) {
|
|
355
|
-
await addOrgAndProjectToSentryCliRc(orgSlug, projectSlug, setupConfig);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
326
|
await addAuthTokenFileToGitIgnore(setupConfig.filename);
|
|
359
327
|
}
|
|
360
328
|
|
|
@@ -456,6 +424,7 @@ export async function ensurePackageIsInstalled(
|
|
|
456
424
|
packageName: string,
|
|
457
425
|
) {
|
|
458
426
|
if (!hasPackageInstalled(packageId, packageJson)) {
|
|
427
|
+
Sentry.setTag('package-installed', false);
|
|
459
428
|
const continueWithoutPackage = await abortIfCancelled(
|
|
460
429
|
clack.confirm({
|
|
461
430
|
message: `${packageName} does not seem to be installed. Do you still want to continue?`,
|
|
@@ -466,6 +435,8 @@ export async function ensurePackageIsInstalled(
|
|
|
466
435
|
if (!continueWithoutPackage) {
|
|
467
436
|
await abort(undefined, 0);
|
|
468
437
|
}
|
|
438
|
+
} else {
|
|
439
|
+
Sentry.setTag('package-installed', true);
|
|
469
440
|
}
|
|
470
441
|
}
|
|
471
442
|
|
|
@@ -778,14 +749,21 @@ async function askForWizardLogin(options: {
|
|
|
778
749
|
async function askForProjectSelection(
|
|
779
750
|
projects: SentryProjectData[],
|
|
780
751
|
): Promise<SentryProjectData> {
|
|
752
|
+
const label = (project: SentryProjectData): string => {
|
|
753
|
+
return `${project.organization.slug}/${project.slug}`;
|
|
754
|
+
};
|
|
755
|
+
const sortedProjects = [...projects];
|
|
756
|
+
sortedProjects.sort((a: SentryProjectData, b: SentryProjectData) => {
|
|
757
|
+
return label(a).localeCompare(label(b));
|
|
758
|
+
});
|
|
781
759
|
const selection: SentryProjectData | symbol = await abortIfCancelled(
|
|
782
760
|
clack.select({
|
|
783
761
|
maxItems: 12,
|
|
784
762
|
message: 'Select your Sentry project.',
|
|
785
|
-
options:
|
|
763
|
+
options: sortedProjects.map((project) => {
|
|
786
764
|
return {
|
|
787
765
|
value: project,
|
|
788
|
-
label:
|
|
766
|
+
label: label(project),
|
|
789
767
|
};
|
|
790
768
|
}),
|
|
791
769
|
}),
|
|
@@ -797,3 +775,192 @@ async function askForProjectSelection(
|
|
|
797
775
|
|
|
798
776
|
return selection;
|
|
799
777
|
}
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* Asks users if they have a config file for @param tool (e.g. Vite).
|
|
781
|
+
* If yes, asks users to specify the path to their config file.
|
|
782
|
+
*
|
|
783
|
+
* Use this helper function as a fallback mechanism if the lookup for
|
|
784
|
+
* a config file with its most usual location/name fails.
|
|
785
|
+
*
|
|
786
|
+
* @param toolName Name of the tool for which we're looking for the config file
|
|
787
|
+
* @param configFileName Name of the most common config file name (e.g. vite.config.js)
|
|
788
|
+
*
|
|
789
|
+
* @returns a user path to the config file or undefined if the user doesn't have a config file
|
|
790
|
+
*/
|
|
791
|
+
export async function askForToolConfigPath(
|
|
792
|
+
toolName: string,
|
|
793
|
+
configFileName: string,
|
|
794
|
+
): Promise<string | undefined> {
|
|
795
|
+
const hasConfig = await abortIfCancelled(
|
|
796
|
+
clack.confirm({
|
|
797
|
+
message: `Do you have a ${toolName} config file (e.g. ${chalk.cyan(
|
|
798
|
+
configFileName,
|
|
799
|
+
)})?`,
|
|
800
|
+
initialValue: true,
|
|
801
|
+
}),
|
|
802
|
+
);
|
|
803
|
+
|
|
804
|
+
if (!hasConfig) {
|
|
805
|
+
return undefined;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
return await abortIfCancelled(
|
|
809
|
+
clack.text({
|
|
810
|
+
message: `Please enter the path to your ${toolName} config file:`,
|
|
811
|
+
placeholder: path.join('.', configFileName),
|
|
812
|
+
validate: (value) => {
|
|
813
|
+
if (!value) {
|
|
814
|
+
return 'Please enter a path.';
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
try {
|
|
818
|
+
fs.accessSync(value);
|
|
819
|
+
} catch {
|
|
820
|
+
return 'Could not access the file at this path.';
|
|
821
|
+
}
|
|
822
|
+
},
|
|
823
|
+
}),
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* Prints copy/paste-able instructions to the console.
|
|
829
|
+
* Afterwards asks the user if they added the code snippet to their file.
|
|
830
|
+
*
|
|
831
|
+
* While there's no point in providing a "no" answer here, it gives users time to fulfill the
|
|
832
|
+
* task before the wizard continues with additional steps.
|
|
833
|
+
*
|
|
834
|
+
* Use this function if you want to show users instructions on how to add/modify
|
|
835
|
+
* code in their file. This is helpful if automatic insertion failed or is not possible/feasible.
|
|
836
|
+
*
|
|
837
|
+
* @param filename the name of the file to which the code snippet should be applied.
|
|
838
|
+
* If a path is provided, only the filename will be used.
|
|
839
|
+
*
|
|
840
|
+
* @param codeSnippet the snippet to be printed. Use {@link makeCodeSnippet} to create the
|
|
841
|
+
* diff-like format for visually highlighting unchanged or modified lines of code.
|
|
842
|
+
*
|
|
843
|
+
* @param hint (optional) a hint to be printed after the main instruction to add
|
|
844
|
+
* the code from @param codeSnippet to their @param filename.
|
|
845
|
+
*
|
|
846
|
+
* More guidelines on copy/paste instructions:
|
|
847
|
+
* @see {@link https://develop.sentry.dev/sdk/setup-wizards/#copy--paste-snippets}
|
|
848
|
+
*
|
|
849
|
+
* TODO: refactor copy paste instructions across different wizards to use this function.
|
|
850
|
+
* this might require adding a custom message parameter to the function
|
|
851
|
+
*/
|
|
852
|
+
export async function showCopyPasteInstructions(
|
|
853
|
+
filename: string,
|
|
854
|
+
codeSnippet: string,
|
|
855
|
+
hint?: string,
|
|
856
|
+
): Promise<void> {
|
|
857
|
+
clack.log.step(
|
|
858
|
+
`Add the following code to your ${chalk.cyan(
|
|
859
|
+
path.basename(filename),
|
|
860
|
+
)} file:${hint ? chalk.dim(` (${chalk.dim(hint)})`) : ''}`,
|
|
861
|
+
);
|
|
862
|
+
|
|
863
|
+
// Padding the code snippet to be printed with a \n at the beginning and end
|
|
864
|
+
// This makes it easier to distinguish the snippet from the rest of the output
|
|
865
|
+
// Intentionally logging directly to console here so that the code can be copied/pasted directly
|
|
866
|
+
// eslint-disable-next-line no-console
|
|
867
|
+
console.log(`\n${codeSnippet}\n`);
|
|
868
|
+
|
|
869
|
+
await abortIfCancelled(
|
|
870
|
+
clack.select({
|
|
871
|
+
message: 'Did you apply the snippet above?',
|
|
872
|
+
options: [{ label: 'Yes, continue!', value: true }],
|
|
873
|
+
initialValue: true,
|
|
874
|
+
}),
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* Callback that exposes formatting helpers for a code snippet.
|
|
880
|
+
* @param unchanged - Formats text as old code.
|
|
881
|
+
* @param plus - Formats text as new code.
|
|
882
|
+
* @param minus - Formats text as removed code.
|
|
883
|
+
*/
|
|
884
|
+
type CodeSnippetFormatter = (
|
|
885
|
+
unchanged: (txt: string) => string,
|
|
886
|
+
plus: (txt: string) => string,
|
|
887
|
+
minus: (txt: string) => string,
|
|
888
|
+
) => string;
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* Crafts a code snippet that can be used to e.g.
|
|
892
|
+
* - print copy/paste instructions to the console
|
|
893
|
+
* - create a new config file.
|
|
894
|
+
*
|
|
895
|
+
* @param colors set this to true if you want the final snippet to be colored.
|
|
896
|
+
* This is useful for printing the snippet to the console as part of copy/paste instructions.
|
|
897
|
+
*
|
|
898
|
+
* @param callback the callback that returns the formatted code snippet.
|
|
899
|
+
* It exposes takes the helper functions for marking code as unchaned, new or removed.
|
|
900
|
+
* These functions no-op if no special formatting should be applied
|
|
901
|
+
* and otherwise apply the appropriate formatting/coloring.
|
|
902
|
+
* (@see {@link CodeSnippetFormatter})
|
|
903
|
+
*
|
|
904
|
+
* @see {@link showCopyPasteInstructions} for the helper with which to display the snippet in the console.
|
|
905
|
+
*
|
|
906
|
+
* @returns a string containing the final, formatted code snippet.
|
|
907
|
+
*/
|
|
908
|
+
export function makeCodeSnippet(
|
|
909
|
+
colors: boolean,
|
|
910
|
+
callback: CodeSnippetFormatter,
|
|
911
|
+
): string {
|
|
912
|
+
const unchanged = (txt: string) => (colors ? chalk.grey(txt) : txt);
|
|
913
|
+
const plus = (txt: string) => (colors ? chalk.greenBright(txt) : txt);
|
|
914
|
+
const minus = (txt: string) => (colors ? chalk.redBright(txt) : txt);
|
|
915
|
+
|
|
916
|
+
return callback(unchanged, plus, minus);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
* Creates a new config file with the given @param filepath and @param codeSnippet.
|
|
921
|
+
*
|
|
922
|
+
* Use this function to create a new config file for users. This is useful
|
|
923
|
+
* when users answered that they don't yet have a config file for a tool.
|
|
924
|
+
*
|
|
925
|
+
* (This doesn't mean that they don't yet have some other way of configuring
|
|
926
|
+
* their tool but we can leave it up to them to figure out how to merge configs
|
|
927
|
+
* here.)
|
|
928
|
+
*
|
|
929
|
+
* @param filepath absolute path to the new config file
|
|
930
|
+
* @param codeSnippet the snippet to be inserted into the file
|
|
931
|
+
* @param moreInformation (optional) the message to be printed after the file was created
|
|
932
|
+
* For example, this can be a link to more information about configuring the tool.
|
|
933
|
+
*
|
|
934
|
+
* @returns true on sucess, false otherwise
|
|
935
|
+
*/
|
|
936
|
+
export async function createNewConfigFile(
|
|
937
|
+
filepath: string,
|
|
938
|
+
codeSnippet: string,
|
|
939
|
+
moreInformation?: string,
|
|
940
|
+
): Promise<boolean> {
|
|
941
|
+
if (!path.isAbsolute(filepath)) {
|
|
942
|
+
debug(`createNewConfigFile: filepath is not absolute: ${filepath}`);
|
|
943
|
+
return false;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
const prettyFilename = chalk.cyan(path.relative(process.cwd(), filepath));
|
|
947
|
+
|
|
948
|
+
try {
|
|
949
|
+
await fs.promises.writeFile(filepath, codeSnippet);
|
|
950
|
+
|
|
951
|
+
clack.log.success(`Added new ${prettyFilename} file.`);
|
|
952
|
+
|
|
953
|
+
if (moreInformation) {
|
|
954
|
+
clack.log.info(chalk.gray(moreInformation));
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
return true;
|
|
958
|
+
} catch (e) {
|
|
959
|
+
debug(e);
|
|
960
|
+
clack.log.warn(
|
|
961
|
+
`Could not create a new ${prettyFilename} file. Please create one manually and follow the instructions below.`,
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
return false;
|
|
966
|
+
}
|
|
@@ -10,38 +10,44 @@ export interface PackageManager {
|
|
|
10
10
|
lockFile: string;
|
|
11
11
|
installCommand: string;
|
|
12
12
|
buildCommand: string;
|
|
13
|
+
/* The command that the package manager uses to run a script from package.json */
|
|
14
|
+
runScriptCommand: string;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
const
|
|
17
|
+
export const BUN: PackageManager = {
|
|
16
18
|
name: 'bun',
|
|
17
19
|
label: 'Bun',
|
|
18
20
|
lockFile: 'bun.lockb',
|
|
19
21
|
installCommand: 'bun add',
|
|
20
|
-
buildCommand: 'bun build',
|
|
22
|
+
buildCommand: 'bun run build',
|
|
23
|
+
runScriptCommand: 'bun run',
|
|
21
24
|
};
|
|
22
|
-
const
|
|
25
|
+
export const YARN: PackageManager = {
|
|
23
26
|
name: 'yarn',
|
|
24
27
|
label: 'Yarn',
|
|
25
28
|
lockFile: 'yarn.lock',
|
|
26
29
|
installCommand: 'yarn add',
|
|
27
30
|
buildCommand: 'yarn build',
|
|
31
|
+
runScriptCommand: 'yarn',
|
|
28
32
|
};
|
|
29
|
-
const
|
|
33
|
+
export const PNPM: PackageManager = {
|
|
30
34
|
name: 'pnpm',
|
|
31
35
|
label: 'PNPM',
|
|
32
36
|
lockFile: 'pnpm-lock.yaml',
|
|
33
37
|
installCommand: 'pnpm add',
|
|
34
38
|
buildCommand: 'pnpm build',
|
|
39
|
+
runScriptCommand: 'pnpm',
|
|
35
40
|
};
|
|
36
|
-
const
|
|
41
|
+
export const NPM: PackageManager = {
|
|
37
42
|
name: 'npm',
|
|
38
43
|
label: 'NPM',
|
|
39
44
|
lockFile: 'package-lock.json',
|
|
40
45
|
installCommand: 'npm add',
|
|
41
46
|
buildCommand: 'npm run build',
|
|
47
|
+
runScriptCommand: 'npm run',
|
|
42
48
|
};
|
|
43
49
|
|
|
44
|
-
export const packageManagers = [
|
|
50
|
+
export const packageManagers = [BUN, YARN, PNPM, NPM];
|
|
45
51
|
|
|
46
52
|
export function detectPackageManger(): PackageManager | null {
|
|
47
53
|
for (const packageManager of packageManagers) {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//@ts-ignore
|
|
2
|
+
import { getLastImportLineLocation } from '../../src/android/code-tools';
|
|
3
|
+
|
|
4
|
+
describe('code-tools', () => {
|
|
5
|
+
describe('getLastImportLineLocation', () => {
|
|
6
|
+
it('returns proper line index', () => {
|
|
7
|
+
const code = `import a.b.c;\n` + `//<insert-location>\n` + `class X {}`;
|
|
8
|
+
expect(getLastImportLineLocation(code)).toBe(
|
|
9
|
+
code.indexOf('//<insert-location>'),
|
|
10
|
+
);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('returns proper line index when static import is used', () => {
|
|
14
|
+
const code =
|
|
15
|
+
`import static a.b.c;\n` + `//<insert-location>\n` + `class X {}`;
|
|
16
|
+
expect(getLastImportLineLocation(code)).toBe(
|
|
17
|
+
code.indexOf('//<insert-location>'),
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('returns proper line index when wildcard import is used', () => {
|
|
22
|
+
const code = `import a.b.*\n` + `//<insert-location>\n` + `class X {}`;
|
|
23
|
+
expect(getLastImportLineLocation(code)).toBe(
|
|
24
|
+
code.indexOf('//<insert-location>'),
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('returns proper line index when alias import is used', () => {
|
|
29
|
+
const code =
|
|
30
|
+
`import static a.b.c as d\n` + `//<insert-location>\n` + `class X {}`;
|
|
31
|
+
expect(getLastImportLineLocation(code)).toBe(
|
|
32
|
+
code.indexOf('//<insert-location>'),
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('returns proper line index when multiple imports are present', () => {
|
|
37
|
+
const code =
|
|
38
|
+
`import static a.b.c as d\n` +
|
|
39
|
+
`import a.b.*\n` +
|
|
40
|
+
`import static a.b.c;\n` +
|
|
41
|
+
`import a.b.c;\n` +
|
|
42
|
+
`//<insert-location>\n` +
|
|
43
|
+
`class X {}`;
|
|
44
|
+
expect(getLastImportLineLocation(code)).toBe(
|
|
45
|
+
code.indexOf('//<insert-location>'),
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
|
|
3
|
+
import { addSentryCommandToBuildCommand } from '../../../src/sourcemaps/tools/sentry-cli';
|
|
4
|
+
|
|
5
|
+
import * as packageManagerHelpers from '../../../src/utils/package-manager';
|
|
6
|
+
|
|
7
|
+
const writeFileSpy = jest
|
|
8
|
+
.spyOn(fs.promises, 'writeFile')
|
|
9
|
+
.mockImplementation(() => Promise.resolve());
|
|
10
|
+
|
|
11
|
+
jest.mock('@clack/prompts', () => {
|
|
12
|
+
return {
|
|
13
|
+
log: {
|
|
14
|
+
info: jest.fn(),
|
|
15
|
+
success: jest.fn(),
|
|
16
|
+
},
|
|
17
|
+
confirm: jest.fn().mockResolvedValue(true),
|
|
18
|
+
isCancel: jest.fn().mockReturnValue(false),
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('addSentryCommandToBuildCommand', () => {
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
jest.clearAllMocks();
|
|
25
|
+
});
|
|
26
|
+
it.each([
|
|
27
|
+
[
|
|
28
|
+
packageManagerHelpers.NPM,
|
|
29
|
+
packageManagerHelpers.PNPM,
|
|
30
|
+
packageManagerHelpers.YARN,
|
|
31
|
+
packageManagerHelpers.BUN,
|
|
32
|
+
],
|
|
33
|
+
])('adds the cli command to the script command (%s)', async (_, pacMan) => {
|
|
34
|
+
jest
|
|
35
|
+
.spyOn(packageManagerHelpers, 'detectPackageManger')
|
|
36
|
+
.mockReturnValue(pacMan);
|
|
37
|
+
const packageJson = {
|
|
38
|
+
scripts: {
|
|
39
|
+
build: 'tsc',
|
|
40
|
+
},
|
|
41
|
+
version: '1.0.0',
|
|
42
|
+
};
|
|
43
|
+
await addSentryCommandToBuildCommand(packageJson);
|
|
44
|
+
expect(writeFileSpy).toHaveBeenCalledWith(
|
|
45
|
+
expect.stringContaining('package.json'),
|
|
46
|
+
expect.stringContaining(
|
|
47
|
+
`tsc && ${pacMan.runScriptCommand} sentry:sourcemaps`,
|
|
48
|
+
),
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import { enableSourcemaps } from '../../../src/sourcemaps/tools/tsc';
|
|
3
|
+
|
|
4
|
+
function updateFileContent(content: string): void {
|
|
5
|
+
fileContent = content;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
let fileContent = '';
|
|
9
|
+
|
|
10
|
+
jest.mock('@clack/prompts', () => {
|
|
11
|
+
return {
|
|
12
|
+
log: {
|
|
13
|
+
info: jest.fn(),
|
|
14
|
+
success: jest.fn(),
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
jest
|
|
20
|
+
.spyOn(fs.promises, 'readFile')
|
|
21
|
+
.mockImplementation(() => Promise.resolve(fileContent));
|
|
22
|
+
|
|
23
|
+
const writeFileSpy = jest
|
|
24
|
+
.spyOn(fs.promises, 'writeFile')
|
|
25
|
+
.mockImplementation(() => Promise.resolve(void 0));
|
|
26
|
+
|
|
27
|
+
describe('enableSourcemaps', () => {
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
fileContent = '';
|
|
30
|
+
jest.clearAllMocks();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it.each([
|
|
34
|
+
[
|
|
35
|
+
'no sourcemaps options',
|
|
36
|
+
`
|
|
37
|
+
/**
|
|
38
|
+
* My TS config with comments
|
|
39
|
+
*/
|
|
40
|
+
{
|
|
41
|
+
"extends": "./tsconfig.build.json",
|
|
42
|
+
|
|
43
|
+
"compilerOptions": {
|
|
44
|
+
// line comment which should stay
|
|
45
|
+
"moduleResolution": "node16",
|
|
46
|
+
"outDir": "dist" // another inline comment
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
"include": [
|
|
50
|
+
"src/**/*",
|
|
51
|
+
"test/**/*"
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
`,
|
|
55
|
+
`
|
|
56
|
+
/**
|
|
57
|
+
* My TS config with comments
|
|
58
|
+
*/
|
|
59
|
+
{
|
|
60
|
+
"extends": "./tsconfig.build.json",
|
|
61
|
+
|
|
62
|
+
"compilerOptions": {
|
|
63
|
+
// line comment which should stay
|
|
64
|
+
"moduleResolution": "node16",
|
|
65
|
+
|
|
66
|
+
// another inline comment
|
|
67
|
+
"outDir": "dist",
|
|
68
|
+
|
|
69
|
+
"sourceMap": true,
|
|
70
|
+
"inlineSources": true,
|
|
71
|
+
|
|
72
|
+
// Set \`sourceRoot\` to "/" to strip the build path prefix
|
|
73
|
+
// from generated source code references.
|
|
74
|
+
// This improves issue grouping in Sentry.
|
|
75
|
+
"sourceRoot": "/"
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
"include": [
|
|
79
|
+
"src/**/*",
|
|
80
|
+
"test/**/*"
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
`,
|
|
84
|
+
],
|
|
85
|
+
[
|
|
86
|
+
'a few sourcemaps options',
|
|
87
|
+
`
|
|
88
|
+
/**
|
|
89
|
+
* My TS config with comments
|
|
90
|
+
*/
|
|
91
|
+
{
|
|
92
|
+
"extends": "./tsconfig.build.json",
|
|
93
|
+
|
|
94
|
+
"compilerOptions": {
|
|
95
|
+
// line comment which should stay
|
|
96
|
+
"moduleResolution": "node16",
|
|
97
|
+
"outDir": "dist", // another inline comment
|
|
98
|
+
"sourceMap": false,
|
|
99
|
+
"sourceRoot": "/src"
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
"include": [
|
|
103
|
+
"src/**/*",
|
|
104
|
+
"test/**/*"
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
`,
|
|
108
|
+
`
|
|
109
|
+
/**
|
|
110
|
+
* My TS config with comments
|
|
111
|
+
*/
|
|
112
|
+
{
|
|
113
|
+
"extends": "./tsconfig.build.json",
|
|
114
|
+
|
|
115
|
+
"compilerOptions": {
|
|
116
|
+
// line comment which should stay
|
|
117
|
+
"moduleResolution": "node16",
|
|
118
|
+
|
|
119
|
+
// another inline comment
|
|
120
|
+
"outDir": "dist",
|
|
121
|
+
|
|
122
|
+
"sourceMap": true,
|
|
123
|
+
|
|
124
|
+
// Set \`sourceRoot\` to "/" to strip the build path prefix
|
|
125
|
+
// from generated source code references.
|
|
126
|
+
// This improves issue grouping in Sentry.
|
|
127
|
+
"sourceRoot": "/",
|
|
128
|
+
|
|
129
|
+
"inlineSources": true
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
"include": [
|
|
133
|
+
"src/**/*",
|
|
134
|
+
"test/**/*"
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
`,
|
|
138
|
+
],
|
|
139
|
+
[
|
|
140
|
+
'no compiler options',
|
|
141
|
+
`
|
|
142
|
+
{
|
|
143
|
+
"include": [
|
|
144
|
+
"src/**/*",
|
|
145
|
+
"test/**/*"
|
|
146
|
+
]
|
|
147
|
+
}
|
|
148
|
+
`,
|
|
149
|
+
`
|
|
150
|
+
{
|
|
151
|
+
"include": [
|
|
152
|
+
"src/**/*",
|
|
153
|
+
"test/**/*"
|
|
154
|
+
],
|
|
155
|
+
|
|
156
|
+
"compilerOptions": {
|
|
157
|
+
"sourceMap": true,
|
|
158
|
+
"inlineSources": true,
|
|
159
|
+
|
|
160
|
+
// Set \`sourceRoot\` to "/" to strip the build path prefix
|
|
161
|
+
// from generated source code references.
|
|
162
|
+
// This improves issue grouping in Sentry.
|
|
163
|
+
"sourceRoot": "/"
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
`,
|
|
167
|
+
],
|
|
168
|
+
])(
|
|
169
|
+
'adds the plugin and enables source maps generation (%s)',
|
|
170
|
+
async (_, originalCode, expectedCode) => {
|
|
171
|
+
updateFileContent(originalCode);
|
|
172
|
+
|
|
173
|
+
const addedCode = await enableSourcemaps('');
|
|
174
|
+
|
|
175
|
+
expect(writeFileSpy).toHaveBeenCalledTimes(1);
|
|
176
|
+
const [[, fileContent]] = writeFileSpy.mock.calls;
|
|
177
|
+
expect(fileContent).toBe(expectedCode);
|
|
178
|
+
expect(addedCode).toBe(true);
|
|
179
|
+
},
|
|
180
|
+
);
|
|
181
|
+
});
|